/*
 * Copyright 1999 SuSE, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of SuSE not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  SuSE makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Keith Packard, SuSE, Inc.
 */

#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include "kdrive.h"

const KdMonitorTiming kdMonitorTimings[] = {
    /*  H       V       Hz      KHz */
    /*  FP      BP      BLANK   POLARITY */

    /* IPAQ modeline:
     *
     * Modeline "320x240"      5.7222 320 337 340 352   240 241 244 254"
     */
    {320, 240, 64, 16256,
     17, 12, 32, KdSyncNegative,
     1, 11, 14, KdSyncNegative,
     },

    /* Other VESA modes */
    {640, 350, 85, 31500,       /* VESA */
     32, 96, 192, KdSyncPositive,       /* 26.413 */
     32, 60, 95, KdSyncNegative,        /* 59.354 */
     },
    {640, 400, 60, 31500,       /* VESA */
     32, 96, 192, KdSyncNegative,       /* 26.413 */
     1, 41, 45, KdSyncPositive, /* 59.354 */
     },
    {720, 400, 85, 35500,       /* VESA */
     36, 108, 216, KdSyncNegative,      /* 37.927 */
     1, 42, 46, KdSyncPositive, /* 85.039 */
     },

    /* Modeline "720x576"     29.000 720  736  800  880   576  577  580  625 */
    {
     720, 576, 52, 32954,       /* PAL Video */
     16, 80, 160, KdSyncPositive,       /* 32.954 */
     1, 45, 49, KdSyncPositive, /* 52.727 */
     },

    /* 640x480 modes */
    {640, 480, 85, 36000,       /* VESA */
     56, 80, 192, KdSyncNegative,       /* 43.269 */
     1, 25, 29, KdSyncNegative, /* 85.008 */
     },
    {640, 480, 75, 31500,       /* VESA */
     16, 120, 200, KdSyncNegative,      /* 37.500 */
     1, 16, 20, KdSyncNegative, /* 75.000 */
     },
    {640, 480, 72, 31500,       /* VESA */
     16, 120, 176, KdSyncNegative,      /* 37.861 */
     1, 20, 24, KdSyncNegative, /* 72.809 */
     },
    {640, 480, 60, 25175,       /* VESA */
     16, 48, 160, KdSyncNegative,       /* 31.469 */
     10, 33, 45, KdSyncNegative,        /* 59.940 */
     },

    /* 800x600 modes */
    {800, 600, 85, 56250,       /* VESA */
     32, 152, 248, KdSyncPositive,      /* 53.674 */
     1, 27, 31, KdSyncPositive, /* 85.061 */
     },
    {800, 600, 75, 49500,       /* VESA */
     16, 160, 256, KdSyncPositive,      /* 46.875 */
     1, 21, 25, KdSyncPositive, /* 75.000 */
     },
    /* DEFAULT */
#define MONITOR_TIMING_DEFAULT	9
    {800, 600, 72, 50000,       /* VESA */
     56, 64, 240, KdSyncPositive,       /* 48.077 */
     37, 23, 66, KdSyncPositive,        /* 72.188 */
     },
    {800, 600, 60, 40000,       /* VESA */
     40, 88, 256, KdSyncPositive,       /* 37.879 */
     1, 23, 28, KdSyncPositive, /* 60.317 */
     },
    {800, 600, 56, 36000,       /* VESA */
     24, 128, 224, KdSyncPositive,      /* 35.156 */
     1, 22, 25, KdSyncPositive, /* 56.250 */
     },

    /* 1024x768 modes */
    {1024, 768, 85, 94500,      /* VESA */
     48, 208, 352, KdSyncPositive,      /* 68.677 */
     1, 36, 40, KdSyncPositive, /* 84.997 */
     },
    {1024, 768, 75, 78750,      /* VESA */
     16, 176, 288, KdSyncPositive,      /* 60.023 */
     1, 28, 32, KdSyncPositive, /* 75.029 */
     },
    {1024, 768, 70, 75000,      /* VESA */
     24, 144, 304, KdSyncNegative,      /* 56.476 */
     3, 29, 38, KdSyncNegative, /* 70.069 */
     },
    {1024, 768, 60, 65000,      /* VESA */
     24, 160, 320, KdSyncNegative,      /* 48.363 */
     3, 29, 38, KdSyncNegative, /* 60.004 */
     },

    /* 1152x864 mode */
    {1152, 864, 75, 108000,     /* VESA */
     64, 256, 448, KdSyncPositive,      /* 67.500 */
     1, 32, 36, KdSyncPositive, /* 75.000 */
     },

    /* 1152x900 modes */
    {1152, 900, 85, 122500,     /* ADDED */
     48, 208, 384, KdSyncPositive,      /* 79.753 */
     1, 32, 38, KdSyncPositive, /* 85.024 */
     },
    {1152, 900, 75, 108250,     /* ADDED */
     32, 208, 384, KdSyncPositive,      /* 70.475 */
     1, 32, 38, KdSyncPositive, /* 75.133 */
     },
    {1152, 900, 70, 100250,     /* ADDED */
     32, 208, 384, KdSyncPositive,      /* 65.267 */
     2, 32, 38, KdSyncPositive, /* 69.581 */
     },
    {1152, 900, 66, 95000,      /* ADDED */
     32, 208, 384, KdSyncPositive,      /* 61.849 */
     1, 32, 38, KdSyncPositive, /* 65.937 */
     },

    /* 1280x854 modes */
    {1280, 854, 103, 12500,     /* ADDED */
     56, 16, 128, KdSyncPositive,       /* 102.554 */
     1, 216, 12, KdSyncPositive,
     },

    /* 1280x960 modes */
    {1280, 960, 85, 148500,     /* VESA */
     64, 224, 448, KdSyncPositive,      /* 85.938 */
     1, 47, 51, KdSyncPositive, /* 85.002 */
     },
    {1280, 960, 60, 108000,     /* VESA */
     96, 312, 520, KdSyncPositive,      /* 60.000 */
     1, 36, 40, KdSyncPositive, /* 60.000 */
     },

    /* 1280x1024 modes */
    {1280, 1024, 85, 157500,    /* VESA */
     64, 224, 448, KdSyncPositive,      /* 91.146 */
     1, 44, 48, KdSyncPositive, /* 85.024 */
     },
    {1280, 1024, 75, 135000,    /* VESA */
     16, 248, 408, KdSyncPositive,      /* 79.976 */
     1, 38, 42, KdSyncPositive, /* 75.025 */
     },
    {1280, 1024, 60, 108000,    /* VESA */
     48, 248, 408, KdSyncPositive,      /* 63.981 */
     1, 38, 42, KdSyncPositive, /* 60.020 */
     },

    /* 1600x1200 modes */
    {1600, 1200, 85, 229500,    /* VESA */
     64, 304, 560, KdSyncPositive,      /* 106.250 */
     1, 46, 50, KdSyncPositive, /* 85.000 */
     },
    {1600, 1200, 75, 202500,    /* VESA */
     64, 304, 560, KdSyncPositive,      /* 93.750 */
     1, 46, 50, KdSyncPositive, /* 75.000 */
     },
    {1600, 1200, 70, 189000,    /* VESA */
     64, 304, 560, KdSyncPositive,      /* 87.500 */
     1, 46, 50, KdSyncPositive, /* 70.000 */
     },
    {1600, 1200, 65, 175500,    /* VESA */
     64, 304, 560, KdSyncPositive,      /* 81.250 */
     1, 46, 50, KdSyncPositive, /* 65.000 */
     },
    {1600, 1200, 60, 162000,    /* VESA */
     64, 304, 560, KdSyncPositive,      /* 75.000 */
     1, 46, 50, KdSyncPositive, /* 60.000 */
     },

    /* 1792x1344 modes */
    {1792, 1344, 85, 301500,    /* ADDED */
     96, 352, 672, KdSyncNegative,      /* 122.362 */
     1, 92, 96, KdSyncPositive, /* 84.974 */
     },
    {1792, 1344, 75, 261000,    /* VESA */
     96, 352, 664, KdSyncNegative,      /* 106.270 */
     1, 69, 73, KdSyncPositive, /* 74.997 */
     },
    {1792, 1344, 60, 204750,    /* VESA */
     128, 328, 656, KdSyncNegative,     /* 83.640 */
     1, 46, 50, KdSyncPositive, /* 60.000 */
     },

#if 0
    {1800, 1012, 75},
    {1906, 1072, 68},
#endif

    /* 1856x1392 modes */
    {1856, 1392, 85, 330500,    /* ADDED */
     160, 352, 736, KdSyncNegative,     /* 127.508 */
     1, 104, 108, KdSyncPositive,       /* 85.001 */
     },
    {1856, 1392, 75, 288000,    /* VESA */
     128, 352, 704, KdSyncNegative,     /* 112.500 */
     1, 104, 108, KdSyncPositive,       /* 75.000 */
     },
    {1856, 1392, 60, 218250,    /* VESA */
     96, 352, 672, KdSyncNegative,      /* 86.333 */
     1, 43, 47, KdSyncPositive, /* 59.995 */
     },

    /* 1920x1440 modes */
    {1920, 1440, 85, 341750,    /* ADDED */
     160, 352, 760, KdSyncNegative,     /* 127.512 */
     1, 56, 60, KdSyncPositive, /* 85.012 */
     },
    {1920, 1440, 75, 297000,    /* VESA */
     144, 352, 720, KdSyncNegative,     /* 112.500 */
     1, 56, 60, KdSyncPositive, /* 75.000 */
     },
    {1920, 1440, 60, 234000,    /* VESA */
     128, 244, 680, KdSyncNegative,     /* 90.000 */
     1, 56, 60, KdSyncPositive, /* 60.000 */
     },
};

#define NUM_MONITOR_TIMINGS (sizeof kdMonitorTimings/sizeof kdMonitorTimings[0])

const int kdNumMonitorTimings = NUM_MONITOR_TIMINGS;

const KdMonitorTiming *
KdFindMode(KdScreenInfo * screen,
           Bool (*supported) (KdScreenInfo *, const KdMonitorTiming *))
{
    int i;
    const KdMonitorTiming *t;

    for (i = 0, t = kdMonitorTimings; i < NUM_MONITOR_TIMINGS; i++, t++) {
        if ((*supported) (screen, t) &&
            t->horizontal == screen->width &&
            t->vertical == screen->height &&
            (!screen->rate || t->rate <= screen->rate)) {
            return t;
        }
    }
    ErrorF("Warning: mode not found, using default\n");
    return &kdMonitorTimings[MONITOR_TIMING_DEFAULT];
}

static const KdMonitorTiming *
kdFindPrevSize(const KdMonitorTiming * old)
{
    const KdMonitorTiming *new, *prev;

    if (old == kdMonitorTimings)
        return 0;
    new = old;
    /*
     * Search for the previous size
     */
    while (new != kdMonitorTimings) {
        new--;
        if (new->horizontal != old->horizontal &&
            new->vertical != old->vertical) {
            break;
        }
    }
    /*
     * Match the refresh rate (<=)
     */
    while (new != kdMonitorTimings) {
        prev = new - 1;
        if (prev->horizontal == new->horizontal &&
            prev->vertical == new->vertical && prev->rate > old->rate) {
            break;
        }
        new--;
    }
    return new;
}

Bool
KdTuneMode(KdScreenInfo * screen,
           Bool (*usable) (KdScreenInfo *),
           Bool (*supported) (KdScreenInfo *, const KdMonitorTiming *))
{
    const KdMonitorTiming *t;

    while (!(*usable) (screen)) {
        /*
         * Fix requested depth and geometry until it works
         */
        if (screen->fb.depth > 16)
            screen->fb.depth = 16;
        else if (screen->fb.depth > 8)
            screen->fb.depth = 8;
        else {
            t = kdFindPrevSize(KdFindMode(screen, supported));
            if (!t)
                return FALSE;
            screen->width = t->horizontal;
            screen->height = t->vertical;
            screen->rate = t->rate;
        }
    }
    return TRUE;
}

#ifdef RANDR
Bool
KdRandRGetInfo(ScreenPtr pScreen,
               int randr,
               Bool (*supported) (ScreenPtr pScreen, const KdMonitorTiming *))
{
    KdScreenPriv(pScreen);
    KdScreenInfo *screen = pScreenPriv->screen;
    int i;
    const KdMonitorTiming *t;

    for (i = 0, t = kdMonitorTimings; i < NUM_MONITOR_TIMINGS; i++, t++) {
        if ((*supported) (pScreen, t)) {
            RRScreenSizePtr pSize;

            pSize = RRRegisterSize(pScreen,
                                   t->horizontal,
                                   t->vertical,
                                   screen->width_mm, screen->height_mm);
            if (!pSize)
                return FALSE;
            if (!RRRegisterRate(pScreen, pSize, t->rate))
                return FALSE;
            if (t->horizontal == screen->width &&
                t->vertical == screen->height && t->rate == screen->rate)
                RRSetCurrentConfig(pScreen, randr, t->rate, pSize);
        }
    }

    return TRUE;
}

const KdMonitorTiming *
KdRandRGetTiming(ScreenPtr pScreen,
                 Bool (*supported) (ScreenPtr pScreen,
                                    const KdMonitorTiming *),
                 int rate, RRScreenSizePtr pSize)
{
    int i;
    const KdMonitorTiming *t;

    for (i = 0, t = kdMonitorTimings; i < NUM_MONITOR_TIMINGS; i++, t++) {
        if (t->horizontal == pSize->width &&
            t->vertical == pSize->height &&
            t->rate == rate && (*supported) (pScreen, t))
            return t;
    }
    return 0;
}
#endif