linux/drivers/video/geode/video_gx.c
<<
>>
Prefs
   1/*
   2 * Geode GX video processor device.
   3 *
   4 *   Copyright (C) 2006 Arcom Control Systems Ltd.
   5 *
   6 *   Portions from AMD's original 2.4 driver:
   7 *     Copyright (C) 2004 Advanced Micro Devices, Inc.
   8 *
   9 *   This program is free software; you can redistribute it and/or modify it
  10 *   under the terms of the GNU General Public License as published by the
  11 *   Free Software Foundation; either version 2 of the License, or (at your
  12 *   option) any later version.
  13 */
  14#include <linux/fb.h>
  15#include <linux/delay.h>
  16#include <asm/io.h>
  17#include <asm/delay.h>
  18#include <asm/msr.h>
  19#include <asm/geode.h>
  20
  21#include "gxfb.h"
  22
  23
  24/*
  25 * Tables of register settings for various DOTCLKs.
  26 */
  27struct gx_pll_entry {
  28        long pixclock; /* ps */
  29        u32 sys_rstpll_bits;
  30        u32 dotpll_value;
  31};
  32
  33#define POSTDIV3 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
  34#define PREMULT2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPREMULT2)
  35#define PREDIV2  ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
  36
  37static const struct gx_pll_entry gx_pll_table_48MHz[] = {
  38        { 40123, POSTDIV3,          0x00000BF2 },       /*  24.9230 */
  39        { 39721, 0,                 0x00000037 },       /*  25.1750 */
  40        { 35308, POSTDIV3|PREMULT2, 0x00000B1A },       /*  28.3220 */
  41        { 31746, POSTDIV3,          0x000002D2 },       /*  31.5000 */
  42        { 27777, POSTDIV3|PREMULT2, 0x00000FE2 },       /*  36.0000 */
  43        { 26666, POSTDIV3,          0x0000057A },       /*  37.5000 */
  44        { 25000, POSTDIV3,          0x0000030A },       /*  40.0000 */
  45        { 22271, 0,                 0x00000063 },       /*  44.9000 */
  46        { 20202, 0,                 0x0000054B },       /*  49.5000 */
  47        { 20000, 0,                 0x0000026E },       /*  50.0000 */
  48        { 19860, PREMULT2,          0x00000037 },       /*  50.3500 */
  49        { 18518, POSTDIV3|PREMULT2, 0x00000B0D },       /*  54.0000 */
  50        { 17777, 0,                 0x00000577 },       /*  56.2500 */
  51        { 17733, 0,                 0x000007F7 },       /*  56.3916 */
  52        { 17653, 0,                 0x0000057B },       /*  56.6444 */
  53        { 16949, PREMULT2,          0x00000707 },       /*  59.0000 */
  54        { 15873, POSTDIV3|PREMULT2, 0x00000B39 },       /*  63.0000 */
  55        { 15384, POSTDIV3|PREMULT2, 0x00000B45 },       /*  65.0000 */
  56        { 14814, POSTDIV3|PREMULT2, 0x00000FC1 },       /*  67.5000 */
  57        { 14124, POSTDIV3,          0x00000561 },       /*  70.8000 */
  58        { 13888, POSTDIV3,          0x000007E1 },       /*  72.0000 */
  59        { 13426, PREMULT2,          0x00000F4A },       /*  74.4810 */
  60        { 13333, 0,                 0x00000052 },       /*  75.0000 */
  61        { 12698, 0,                 0x00000056 },       /*  78.7500 */
  62        { 12500, POSTDIV3|PREMULT2, 0x00000709 },       /*  80.0000 */
  63        { 11135, PREMULT2,          0x00000262 },       /*  89.8000 */
  64        { 10582, 0,                 0x000002D2 },       /*  94.5000 */
  65        { 10101, PREMULT2,          0x00000B4A },       /*  99.0000 */
  66        { 10000, PREMULT2,          0x00000036 },       /* 100.0000 */
  67        {  9259, 0,                 0x000007E2 },       /* 108.0000 */
  68        {  8888, 0,                 0x000007F6 },       /* 112.5000 */
  69        {  7692, POSTDIV3|PREMULT2, 0x00000FB0 },       /* 130.0000 */
  70        {  7407, POSTDIV3|PREMULT2, 0x00000B50 },       /* 135.0000 */
  71        {  6349, 0,                 0x00000055 },       /* 157.5000 */
  72        {  6172, 0,                 0x000009C1 },       /* 162.0000 */
  73        {  5787, PREMULT2,          0x0000002D },       /* 172.798  */
  74        {  5698, 0,                 0x000002C1 },       /* 175.5000 */
  75        {  5291, 0,                 0x000002D1 },       /* 189.0000 */
  76        {  4938, 0,                 0x00000551 },       /* 202.5000 */
  77        {  4357, 0,                 0x0000057D },       /* 229.5000 */
  78};
  79
  80static const struct gx_pll_entry gx_pll_table_14MHz[] = {
  81        { 39721, 0, 0x00000037 },       /*  25.1750 */
  82        { 35308, 0, 0x00000B7B },       /*  28.3220 */
  83        { 31746, 0, 0x000004D3 },       /*  31.5000 */
  84        { 27777, 0, 0x00000BE3 },       /*  36.0000 */
  85        { 26666, 0, 0x0000074F },       /*  37.5000 */
  86        { 25000, 0, 0x0000050B },       /*  40.0000 */
  87        { 22271, 0, 0x00000063 },       /*  44.9000 */
  88        { 20202, 0, 0x0000054B },       /*  49.5000 */
  89        { 20000, 0, 0x0000026E },       /*  50.0000 */
  90        { 19860, 0, 0x000007C3 },       /*  50.3500 */
  91        { 18518, 0, 0x000007E3 },       /*  54.0000 */
  92        { 17777, 0, 0x00000577 },       /*  56.2500 */
  93        { 17733, 0, 0x000002FB },       /*  56.3916 */
  94        { 17653, 0, 0x0000057B },       /*  56.6444 */
  95        { 16949, 0, 0x0000058B },       /*  59.0000 */
  96        { 15873, 0, 0x0000095E },       /*  63.0000 */
  97        { 15384, 0, 0x0000096A },       /*  65.0000 */
  98        { 14814, 0, 0x00000BC2 },       /*  67.5000 */
  99        { 14124, 0, 0x0000098A },       /*  70.8000 */
 100        { 13888, 0, 0x00000BE2 },       /*  72.0000 */
 101        { 13333, 0, 0x00000052 },       /*  75.0000 */
 102        { 12698, 0, 0x00000056 },       /*  78.7500 */
 103        { 12500, 0, 0x0000050A },       /*  80.0000 */
 104        { 11135, 0, 0x0000078E },       /*  89.8000 */
 105        { 10582, 0, 0x000002D2 },       /*  94.5000 */
 106        { 10101, 0, 0x000011F6 },       /*  99.0000 */
 107        { 10000, 0, 0x0000054E },       /* 100.0000 */
 108        {  9259, 0, 0x000007E2 },       /* 108.0000 */
 109        {  8888, 0, 0x000002FA },       /* 112.5000 */
 110        {  7692, 0, 0x00000BB1 },       /* 130.0000 */
 111        {  7407, 0, 0x00000975 },       /* 135.0000 */
 112        {  6349, 0, 0x00000055 },       /* 157.5000 */
 113        {  6172, 0, 0x000009C1 },       /* 162.0000 */
 114        {  5698, 0, 0x000002C1 },       /* 175.5000 */
 115        {  5291, 0, 0x00000539 },       /* 189.0000 */
 116        {  4938, 0, 0x00000551 },       /* 202.5000 */
 117        {  4357, 0, 0x0000057D },       /* 229.5000 */
 118};
 119
 120void gx_set_dclk_frequency(struct fb_info *info)
 121{
 122        const struct gx_pll_entry *pll_table;
 123        int pll_table_len;
 124        int i, best_i;
 125        long min, diff;
 126        u64 dotpll, sys_rstpll;
 127        int timeout = 1000;
 128
 129        /* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */
 130        if (cpu_data(0).x86_mask == 1) {
 131                pll_table = gx_pll_table_14MHz;
 132                pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz);
 133        } else {
 134                pll_table = gx_pll_table_48MHz;
 135                pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz);
 136        }
 137
 138        /* Search the table for the closest pixclock. */
 139        best_i = 0;
 140        min = abs(pll_table[0].pixclock - info->var.pixclock);
 141        for (i = 1; i < pll_table_len; i++) {
 142                diff = abs(pll_table[i].pixclock - info->var.pixclock);
 143                if (diff < min) {
 144                        min = diff;
 145                        best_i = i;
 146                }
 147        }
 148
 149        rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
 150        rdmsrl(MSR_GLCP_DOTPLL, dotpll);
 151
 152        /* Program new M, N and P. */
 153        dotpll &= 0x00000000ffffffffull;
 154        dotpll |= (u64)pll_table[best_i].dotpll_value << 32;
 155        dotpll |= MSR_GLCP_DOTPLL_DOTRESET;
 156        dotpll &= ~MSR_GLCP_DOTPLL_BYPASS;
 157
 158        wrmsrl(MSR_GLCP_DOTPLL, dotpll);
 159
 160        /* Program dividers. */
 161        sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2
 162                         | MSR_GLCP_SYS_RSTPLL_DOTPREMULT2
 163                         | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 );
 164        sys_rstpll |= pll_table[best_i].sys_rstpll_bits;
 165
 166        wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
 167
 168        /* Clear reset bit to start PLL. */
 169        dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET);
 170        wrmsrl(MSR_GLCP_DOTPLL, dotpll);
 171
 172        /* Wait for LOCK bit. */
 173        do {
 174                rdmsrl(MSR_GLCP_DOTPLL, dotpll);
 175        } while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK));
 176}
 177
 178static void
 179gx_configure_tft(struct fb_info *info)
 180{
 181        struct gxfb_par *par = info->par;
 182        unsigned long val;
 183        unsigned long fp;
 184
 185        /* Set up the DF pad select MSR */
 186
 187        rdmsrl(MSR_GX_MSR_PADSEL, val);
 188        val &= ~MSR_GX_MSR_PADSEL_MASK;
 189        val |= MSR_GX_MSR_PADSEL_TFT;
 190        wrmsrl(MSR_GX_MSR_PADSEL, val);
 191
 192        /* Turn off the panel */
 193
 194        fp = read_fp(par, FP_PM);
 195        fp &= ~FP_PM_P;
 196        write_fp(par, FP_PM, fp);
 197
 198        /* Set timing 1 */
 199
 200        fp = read_fp(par, FP_PT1);
 201        fp &= FP_PT1_VSIZE_MASK;
 202        fp |= info->var.yres << FP_PT1_VSIZE_SHIFT;
 203        write_fp(par, FP_PT1, fp);
 204
 205        /* Timing 2 */
 206        /* Set bits that are always on for TFT */
 207
 208        fp = 0x0F100000;
 209
 210        /* Configure sync polarity */
 211
 212        if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
 213                fp |= FP_PT2_VSP;
 214
 215        if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
 216                fp |= FP_PT2_HSP;
 217
 218        write_fp(par, FP_PT2, fp);
 219
 220        /*  Set the dither control */
 221        write_fp(par, FP_DFC, FP_DFC_NFI);
 222
 223        /* Enable the FP data and power (in case the BIOS didn't) */
 224
 225        fp = read_vp(par, VP_DCFG);
 226        fp |= VP_DCFG_FP_PWR_EN | VP_DCFG_FP_DATA_EN;
 227        write_vp(par, VP_DCFG, fp);
 228
 229        /* Unblank the panel */
 230
 231        fp = read_fp(par, FP_PM);
 232        fp |= FP_PM_P;
 233        write_fp(par, FP_PM, fp);
 234}
 235
 236void gx_configure_display(struct fb_info *info)
 237{
 238        struct gxfb_par *par = info->par;
 239        u32 dcfg, misc;
 240
 241        /* Write the display configuration */
 242        dcfg = read_vp(par, VP_DCFG);
 243
 244        /* Disable hsync and vsync */
 245        dcfg &= ~(VP_DCFG_VSYNC_EN | VP_DCFG_HSYNC_EN);
 246        write_vp(par, VP_DCFG, dcfg);
 247
 248        /* Clear bits from existing mode. */
 249        dcfg &= ~(VP_DCFG_CRT_SYNC_SKW
 250                  | VP_DCFG_CRT_HSYNC_POL   | VP_DCFG_CRT_VSYNC_POL
 251                  | VP_DCFG_VSYNC_EN        | VP_DCFG_HSYNC_EN);
 252
 253        /* Set default sync skew.  */
 254        dcfg |= VP_DCFG_CRT_SYNC_SKW_DEFAULT;
 255
 256        /* Enable hsync and vsync. */
 257        dcfg |= VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN;
 258
 259        misc = read_vp(par, VP_MISC);
 260
 261        /* Disable gamma correction */
 262        misc |= VP_MISC_GAM_EN;
 263
 264        if (par->enable_crt) {
 265
 266                /* Power up the CRT DACs */
 267                misc &= ~(VP_MISC_APWRDN | VP_MISC_DACPWRDN);
 268                write_vp(par, VP_MISC, misc);
 269
 270                /* Only change the sync polarities if we are running
 271                 * in CRT mode.  The FP polarities will be handled in
 272                 * gxfb_configure_tft */
 273                if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
 274                        dcfg |= VP_DCFG_CRT_HSYNC_POL;
 275                if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
 276                        dcfg |= VP_DCFG_CRT_VSYNC_POL;
 277        } else {
 278                /* Power down the CRT DACs if in FP mode */
 279                misc |= (VP_MISC_APWRDN | VP_MISC_DACPWRDN);
 280                write_vp(par, VP_MISC, misc);
 281        }
 282
 283        /* Enable the display logic */
 284        /* Set up the DACS to blank normally */
 285
 286        dcfg |= VP_DCFG_CRT_EN | VP_DCFG_DAC_BL_EN;
 287
 288        /* Enable the external DAC VREF? */
 289
 290        write_vp(par, VP_DCFG, dcfg);
 291
 292        /* Set up the flat panel (if it is enabled) */
 293
 294        if (par->enable_crt == 0)
 295                gx_configure_tft(info);
 296}
 297
 298int gx_blank_display(struct fb_info *info, int blank_mode)
 299{
 300        struct gxfb_par *par = info->par;
 301        u32 dcfg, fp_pm;
 302        int blank, hsync, vsync, crt;
 303
 304        /* CRT power saving modes. */
 305        switch (blank_mode) {
 306        case FB_BLANK_UNBLANK:
 307                blank = 0; hsync = 1; vsync = 1; crt = 1;
 308                break;
 309        case FB_BLANK_NORMAL:
 310                blank = 1; hsync = 1; vsync = 1; crt = 1;
 311                break;
 312        case FB_BLANK_VSYNC_SUSPEND:
 313                blank = 1; hsync = 1; vsync = 0; crt = 1;
 314                break;
 315        case FB_BLANK_HSYNC_SUSPEND:
 316                blank = 1; hsync = 0; vsync = 1; crt = 1;
 317                break;
 318        case FB_BLANK_POWERDOWN:
 319                blank = 1; hsync = 0; vsync = 0; crt = 0;
 320                break;
 321        default:
 322                return -EINVAL;
 323        }
 324        dcfg = read_vp(par, VP_DCFG);
 325        dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
 326                        VP_DCFG_CRT_EN);
 327        if (!blank)
 328                dcfg |= VP_DCFG_DAC_BL_EN;
 329        if (hsync)
 330                dcfg |= VP_DCFG_HSYNC_EN;
 331        if (vsync)
 332                dcfg |= VP_DCFG_VSYNC_EN;
 333        if (crt)
 334                dcfg |= VP_DCFG_CRT_EN;
 335        write_vp(par, VP_DCFG, dcfg);
 336
 337        /* Power on/off flat panel. */
 338
 339        if (par->enable_crt == 0) {
 340                fp_pm = read_fp(par, FP_PM);
 341                if (blank_mode == FB_BLANK_POWERDOWN)
 342                        fp_pm &= ~FP_PM_P;
 343                else
 344                        fp_pm |= FP_PM_P;
 345                write_fp(par, FP_PM, fp_pm);
 346        }
 347
 348        return 0;
 349}
 350