1f7018c21STomi Valkeinen /* 2f7018c21STomi Valkeinen * intelfb 3f7018c21STomi Valkeinen * 4f7018c21STomi Valkeinen * Linux framebuffer driver for Intel(R) 865G integrated graphics chips. 5f7018c21STomi Valkeinen * 6f7018c21STomi Valkeinen * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org> 7f7018c21STomi Valkeinen * 2004 Sylvain Meyer 8f7018c21STomi Valkeinen * 9f7018c21STomi Valkeinen * This driver consists of two parts. The first part (intelfbdrv.c) provides 10f7018c21STomi Valkeinen * the basic fbdev interfaces, is derived in part from the radeonfb and 11f7018c21STomi Valkeinen * vesafb drivers, and is covered by the GPL. The second part (intelfbhw.c) 12f7018c21STomi Valkeinen * provides the code to program the hardware. Most of it is derived from 13f7018c21STomi Valkeinen * the i810/i830 XFree86 driver. The HW-specific code is covered here 14f7018c21STomi Valkeinen * under a dual license (GPL and MIT/XFree86 license). 15f7018c21STomi Valkeinen * 16f7018c21STomi Valkeinen * Author: David Dawes 17f7018c21STomi Valkeinen * 18f7018c21STomi Valkeinen */ 19f7018c21STomi Valkeinen 20f7018c21STomi Valkeinen /* $DHD: intelfb/intelfbhw.c,v 1.9 2003/06/27 15:06:25 dawes Exp $ */ 21f7018c21STomi Valkeinen 22f7018c21STomi Valkeinen #include <linux/module.h> 23f7018c21STomi Valkeinen #include <linux/kernel.h> 24f7018c21STomi Valkeinen #include <linux/errno.h> 25f7018c21STomi Valkeinen #include <linux/string.h> 26f7018c21STomi Valkeinen #include <linux/mm.h> 27f7018c21STomi Valkeinen #include <linux/delay.h> 28f7018c21STomi Valkeinen #include <linux/fb.h> 29f7018c21STomi Valkeinen #include <linux/ioport.h> 30f7018c21STomi Valkeinen #include <linux/init.h> 31f7018c21STomi Valkeinen #include <linux/pci.h> 32f7018c21STomi Valkeinen #include <linux/vmalloc.h> 33f7018c21STomi Valkeinen #include <linux/pagemap.h> 34f7018c21STomi Valkeinen #include <linux/interrupt.h> 35f7018c21STomi Valkeinen 36f7018c21STomi Valkeinen #include <asm/io.h> 37f7018c21STomi Valkeinen 38f7018c21STomi Valkeinen #include "intelfb.h" 39f7018c21STomi Valkeinen #include "intelfbhw.h" 40f7018c21STomi Valkeinen 41f7018c21STomi Valkeinen struct pll_min_max { 42f7018c21STomi Valkeinen int min_m, max_m, min_m1, max_m1; 43f7018c21STomi Valkeinen int min_m2, max_m2, min_n, max_n; 44f7018c21STomi Valkeinen int min_p, max_p, min_p1, max_p1; 45f7018c21STomi Valkeinen int min_vco, max_vco, p_transition_clk, ref_clk; 46f7018c21STomi Valkeinen int p_inc_lo, p_inc_hi; 47f7018c21STomi Valkeinen }; 48f7018c21STomi Valkeinen 49f7018c21STomi Valkeinen #define PLLS_I8xx 0 50f7018c21STomi Valkeinen #define PLLS_I9xx 1 51f7018c21STomi Valkeinen #define PLLS_MAX 2 52f7018c21STomi Valkeinen 53f7018c21STomi Valkeinen static struct pll_min_max plls[PLLS_MAX] = { 54f7018c21STomi Valkeinen { 108, 140, 18, 26, 55f7018c21STomi Valkeinen 6, 16, 3, 16, 56f7018c21STomi Valkeinen 4, 128, 0, 31, 57f7018c21STomi Valkeinen 930000, 1400000, 165000, 48000, 58f7018c21STomi Valkeinen 4, 2 }, /* I8xx */ 59f7018c21STomi Valkeinen 60f7018c21STomi Valkeinen { 75, 120, 10, 20, 61f7018c21STomi Valkeinen 5, 9, 4, 7, 62f7018c21STomi Valkeinen 5, 80, 1, 8, 63f7018c21STomi Valkeinen 1400000, 2800000, 200000, 96000, 64f7018c21STomi Valkeinen 10, 5 } /* I9xx */ 65f7018c21STomi Valkeinen }; 66f7018c21STomi Valkeinen 67f7018c21STomi Valkeinen int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) 68f7018c21STomi Valkeinen { 69f7018c21STomi Valkeinen u32 tmp; 70f7018c21STomi Valkeinen if (!pdev || !dinfo) 71f7018c21STomi Valkeinen return 1; 72f7018c21STomi Valkeinen 73f7018c21STomi Valkeinen switch (pdev->device) { 74f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_830M: 75f7018c21STomi Valkeinen dinfo->name = "Intel(R) 830M"; 76f7018c21STomi Valkeinen dinfo->chipset = INTEL_830M; 77f7018c21STomi Valkeinen dinfo->mobile = 1; 78f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I8xx; 79f7018c21STomi Valkeinen return 0; 80f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_845G: 81f7018c21STomi Valkeinen dinfo->name = "Intel(R) 845G"; 82f7018c21STomi Valkeinen dinfo->chipset = INTEL_845G; 83f7018c21STomi Valkeinen dinfo->mobile = 0; 84f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I8xx; 85f7018c21STomi Valkeinen return 0; 86f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_854: 87f7018c21STomi Valkeinen dinfo->mobile = 1; 88f7018c21STomi Valkeinen dinfo->name = "Intel(R) 854"; 89f7018c21STomi Valkeinen dinfo->chipset = INTEL_854; 90f7018c21STomi Valkeinen return 0; 91f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_85XGM: 92f7018c21STomi Valkeinen tmp = 0; 93f7018c21STomi Valkeinen dinfo->mobile = 1; 94f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I8xx; 95f7018c21STomi Valkeinen pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp); 96f7018c21STomi Valkeinen switch ((tmp >> INTEL_85X_VARIANT_SHIFT) & 97f7018c21STomi Valkeinen INTEL_85X_VARIANT_MASK) { 98f7018c21STomi Valkeinen case INTEL_VAR_855GME: 99f7018c21STomi Valkeinen dinfo->name = "Intel(R) 855GME"; 100f7018c21STomi Valkeinen dinfo->chipset = INTEL_855GME; 101f7018c21STomi Valkeinen return 0; 102f7018c21STomi Valkeinen case INTEL_VAR_855GM: 103f7018c21STomi Valkeinen dinfo->name = "Intel(R) 855GM"; 104f7018c21STomi Valkeinen dinfo->chipset = INTEL_855GM; 105f7018c21STomi Valkeinen return 0; 106f7018c21STomi Valkeinen case INTEL_VAR_852GME: 107f7018c21STomi Valkeinen dinfo->name = "Intel(R) 852GME"; 108f7018c21STomi Valkeinen dinfo->chipset = INTEL_852GME; 109f7018c21STomi Valkeinen return 0; 110f7018c21STomi Valkeinen case INTEL_VAR_852GM: 111f7018c21STomi Valkeinen dinfo->name = "Intel(R) 852GM"; 112f7018c21STomi Valkeinen dinfo->chipset = INTEL_852GM; 113f7018c21STomi Valkeinen return 0; 114f7018c21STomi Valkeinen default: 115f7018c21STomi Valkeinen dinfo->name = "Intel(R) 852GM/855GM"; 116f7018c21STomi Valkeinen dinfo->chipset = INTEL_85XGM; 117f7018c21STomi Valkeinen return 0; 118f7018c21STomi Valkeinen } 119f7018c21STomi Valkeinen break; 120f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_865G: 121f7018c21STomi Valkeinen dinfo->name = "Intel(R) 865G"; 122f7018c21STomi Valkeinen dinfo->chipset = INTEL_865G; 123f7018c21STomi Valkeinen dinfo->mobile = 0; 124f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I8xx; 125f7018c21STomi Valkeinen return 0; 126f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_915G: 127f7018c21STomi Valkeinen dinfo->name = "Intel(R) 915G"; 128f7018c21STomi Valkeinen dinfo->chipset = INTEL_915G; 129f7018c21STomi Valkeinen dinfo->mobile = 0; 130f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 131f7018c21STomi Valkeinen return 0; 132f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_915GM: 133f7018c21STomi Valkeinen dinfo->name = "Intel(R) 915GM"; 134f7018c21STomi Valkeinen dinfo->chipset = INTEL_915GM; 135f7018c21STomi Valkeinen dinfo->mobile = 1; 136f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 137f7018c21STomi Valkeinen return 0; 138f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945G: 139f7018c21STomi Valkeinen dinfo->name = "Intel(R) 945G"; 140f7018c21STomi Valkeinen dinfo->chipset = INTEL_945G; 141f7018c21STomi Valkeinen dinfo->mobile = 0; 142f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 143f7018c21STomi Valkeinen return 0; 144f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945GM: 145f7018c21STomi Valkeinen dinfo->name = "Intel(R) 945GM"; 146f7018c21STomi Valkeinen dinfo->chipset = INTEL_945GM; 147f7018c21STomi Valkeinen dinfo->mobile = 1; 148f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 149f7018c21STomi Valkeinen return 0; 150f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945GME: 151f7018c21STomi Valkeinen dinfo->name = "Intel(R) 945GME"; 152f7018c21STomi Valkeinen dinfo->chipset = INTEL_945GME; 153f7018c21STomi Valkeinen dinfo->mobile = 1; 154f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 155f7018c21STomi Valkeinen return 0; 156f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_965G: 157f7018c21STomi Valkeinen dinfo->name = "Intel(R) 965G"; 158f7018c21STomi Valkeinen dinfo->chipset = INTEL_965G; 159f7018c21STomi Valkeinen dinfo->mobile = 0; 160f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 161f7018c21STomi Valkeinen return 0; 162f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_965GM: 163f7018c21STomi Valkeinen dinfo->name = "Intel(R) 965GM"; 164f7018c21STomi Valkeinen dinfo->chipset = INTEL_965GM; 165f7018c21STomi Valkeinen dinfo->mobile = 1; 166f7018c21STomi Valkeinen dinfo->pll_index = PLLS_I9xx; 167f7018c21STomi Valkeinen return 0; 168f7018c21STomi Valkeinen default: 169f7018c21STomi Valkeinen return 1; 170f7018c21STomi Valkeinen } 171f7018c21STomi Valkeinen } 172f7018c21STomi Valkeinen 173f7018c21STomi Valkeinen int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, 174f7018c21STomi Valkeinen int *stolen_size) 175f7018c21STomi Valkeinen { 176f7018c21STomi Valkeinen struct pci_dev *bridge_dev; 177f7018c21STomi Valkeinen u16 tmp; 178f7018c21STomi Valkeinen int stolen_overhead; 179f7018c21STomi Valkeinen 180f7018c21STomi Valkeinen if (!pdev || !aperture_size || !stolen_size) 181f7018c21STomi Valkeinen return 1; 182f7018c21STomi Valkeinen 183f7018c21STomi Valkeinen /* Find the bridge device. It is always 0:0.0 */ 184f7018c21STomi Valkeinen if (!(bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)))) { 185f7018c21STomi Valkeinen ERR_MSG("cannot find bridge device\n"); 186f7018c21STomi Valkeinen return 1; 187f7018c21STomi Valkeinen } 188f7018c21STomi Valkeinen 189f7018c21STomi Valkeinen /* Get the fb aperture size and "stolen" memory amount. */ 190f7018c21STomi Valkeinen tmp = 0; 191f7018c21STomi Valkeinen pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp); 192f7018c21STomi Valkeinen pci_dev_put(bridge_dev); 193f7018c21STomi Valkeinen 194f7018c21STomi Valkeinen switch (pdev->device) { 195f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_915G: 196f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_915GM: 197f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945G: 198f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945GM: 199f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_945GME: 200f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_965G: 201f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_965GM: 202f7018c21STomi Valkeinen /* 915, 945 and 965 chipsets support a 256MB aperture. 203f7018c21STomi Valkeinen Aperture size is determined by inspected the 204f7018c21STomi Valkeinen base address of the aperture. */ 205f7018c21STomi Valkeinen if (pci_resource_start(pdev, 2) & 0x08000000) 206f7018c21STomi Valkeinen *aperture_size = MB(128); 207f7018c21STomi Valkeinen else 208f7018c21STomi Valkeinen *aperture_size = MB(256); 209f7018c21STomi Valkeinen break; 210f7018c21STomi Valkeinen default: 211f7018c21STomi Valkeinen if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M) 212f7018c21STomi Valkeinen *aperture_size = MB(64); 213f7018c21STomi Valkeinen else 214f7018c21STomi Valkeinen *aperture_size = MB(128); 215f7018c21STomi Valkeinen break; 216f7018c21STomi Valkeinen } 217f7018c21STomi Valkeinen 218f7018c21STomi Valkeinen /* Stolen memory size is reduced by the GTT and the popup. 219f7018c21STomi Valkeinen GTT is 1K per MB of aperture size, and popup is 4K. */ 220f7018c21STomi Valkeinen stolen_overhead = (*aperture_size / MB(1)) + 4; 221f7018c21STomi Valkeinen switch(pdev->device) { 222f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_830M: 223f7018c21STomi Valkeinen case PCI_DEVICE_ID_INTEL_845G: 224f7018c21STomi Valkeinen switch (tmp & INTEL_830_GMCH_GMS_MASK) { 225f7018c21STomi Valkeinen case INTEL_830_GMCH_GMS_STOLEN_512: 226f7018c21STomi Valkeinen *stolen_size = KB(512) - KB(stolen_overhead); 227f7018c21STomi Valkeinen return 0; 228f7018c21STomi Valkeinen case INTEL_830_GMCH_GMS_STOLEN_1024: 229f7018c21STomi Valkeinen *stolen_size = MB(1) - KB(stolen_overhead); 230f7018c21STomi Valkeinen return 0; 231f7018c21STomi Valkeinen case INTEL_830_GMCH_GMS_STOLEN_8192: 232f7018c21STomi Valkeinen *stolen_size = MB(8) - KB(stolen_overhead); 233f7018c21STomi Valkeinen return 0; 234f7018c21STomi Valkeinen case INTEL_830_GMCH_GMS_LOCAL: 235f7018c21STomi Valkeinen ERR_MSG("only local memory found\n"); 236f7018c21STomi Valkeinen return 1; 237f7018c21STomi Valkeinen case INTEL_830_GMCH_GMS_DISABLED: 238f7018c21STomi Valkeinen ERR_MSG("video memory is disabled\n"); 239f7018c21STomi Valkeinen return 1; 240f7018c21STomi Valkeinen default: 241f7018c21STomi Valkeinen ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n", 242f7018c21STomi Valkeinen tmp & INTEL_830_GMCH_GMS_MASK); 243f7018c21STomi Valkeinen return 1; 244f7018c21STomi Valkeinen } 245f7018c21STomi Valkeinen break; 246f7018c21STomi Valkeinen default: 247f7018c21STomi Valkeinen switch (tmp & INTEL_855_GMCH_GMS_MASK) { 248f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_STOLEN_1M: 249f7018c21STomi Valkeinen *stolen_size = MB(1) - KB(stolen_overhead); 250f7018c21STomi Valkeinen return 0; 251f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_STOLEN_4M: 252f7018c21STomi Valkeinen *stolen_size = MB(4) - KB(stolen_overhead); 253f7018c21STomi Valkeinen return 0; 254f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_STOLEN_8M: 255f7018c21STomi Valkeinen *stolen_size = MB(8) - KB(stolen_overhead); 256f7018c21STomi Valkeinen return 0; 257f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_STOLEN_16M: 258f7018c21STomi Valkeinen *stolen_size = MB(16) - KB(stolen_overhead); 259f7018c21STomi Valkeinen return 0; 260f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_STOLEN_32M: 261f7018c21STomi Valkeinen *stolen_size = MB(32) - KB(stolen_overhead); 262f7018c21STomi Valkeinen return 0; 263f7018c21STomi Valkeinen case INTEL_915G_GMCH_GMS_STOLEN_48M: 264f7018c21STomi Valkeinen *stolen_size = MB(48) - KB(stolen_overhead); 265f7018c21STomi Valkeinen return 0; 266f7018c21STomi Valkeinen case INTEL_915G_GMCH_GMS_STOLEN_64M: 267f7018c21STomi Valkeinen *stolen_size = MB(64) - KB(stolen_overhead); 268f7018c21STomi Valkeinen return 0; 269f7018c21STomi Valkeinen case INTEL_855_GMCH_GMS_DISABLED: 270f7018c21STomi Valkeinen ERR_MSG("video memory is disabled\n"); 271f7018c21STomi Valkeinen return 0; 272f7018c21STomi Valkeinen default: 273f7018c21STomi Valkeinen ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n", 274f7018c21STomi Valkeinen tmp & INTEL_855_GMCH_GMS_MASK); 275f7018c21STomi Valkeinen return 1; 276f7018c21STomi Valkeinen } 277f7018c21STomi Valkeinen } 278f7018c21STomi Valkeinen } 279f7018c21STomi Valkeinen 280f7018c21STomi Valkeinen int intelfbhw_check_non_crt(struct intelfb_info *dinfo) 281f7018c21STomi Valkeinen { 282f7018c21STomi Valkeinen int dvo = 0; 283f7018c21STomi Valkeinen 284f7018c21STomi Valkeinen if (INREG(LVDS) & PORT_ENABLE) 285f7018c21STomi Valkeinen dvo |= LVDS_PORT; 286f7018c21STomi Valkeinen if (INREG(DVOA) & PORT_ENABLE) 287f7018c21STomi Valkeinen dvo |= DVOA_PORT; 288f7018c21STomi Valkeinen if (INREG(DVOB) & PORT_ENABLE) 289f7018c21STomi Valkeinen dvo |= DVOB_PORT; 290f7018c21STomi Valkeinen if (INREG(DVOC) & PORT_ENABLE) 291f7018c21STomi Valkeinen dvo |= DVOC_PORT; 292f7018c21STomi Valkeinen 293f7018c21STomi Valkeinen return dvo; 294f7018c21STomi Valkeinen } 295f7018c21STomi Valkeinen 296f7018c21STomi Valkeinen const char * intelfbhw_dvo_to_string(int dvo) 297f7018c21STomi Valkeinen { 298f7018c21STomi Valkeinen if (dvo & DVOA_PORT) 299f7018c21STomi Valkeinen return "DVO port A"; 300f7018c21STomi Valkeinen else if (dvo & DVOB_PORT) 301f7018c21STomi Valkeinen return "DVO port B"; 302f7018c21STomi Valkeinen else if (dvo & DVOC_PORT) 303f7018c21STomi Valkeinen return "DVO port C"; 304f7018c21STomi Valkeinen else if (dvo & LVDS_PORT) 305f7018c21STomi Valkeinen return "LVDS port"; 306f7018c21STomi Valkeinen else 307f7018c21STomi Valkeinen return NULL; 308f7018c21STomi Valkeinen } 309f7018c21STomi Valkeinen 310f7018c21STomi Valkeinen 311f7018c21STomi Valkeinen int intelfbhw_validate_mode(struct intelfb_info *dinfo, 312f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 313f7018c21STomi Valkeinen { 314f7018c21STomi Valkeinen int bytes_per_pixel; 315f7018c21STomi Valkeinen int tmp; 316f7018c21STomi Valkeinen 317f7018c21STomi Valkeinen #if VERBOSE > 0 318f7018c21STomi Valkeinen DBG_MSG("intelfbhw_validate_mode\n"); 319f7018c21STomi Valkeinen #endif 320f7018c21STomi Valkeinen 321f7018c21STomi Valkeinen bytes_per_pixel = var->bits_per_pixel / 8; 322f7018c21STomi Valkeinen if (bytes_per_pixel == 3) 323f7018c21STomi Valkeinen bytes_per_pixel = 4; 324f7018c21STomi Valkeinen 325f7018c21STomi Valkeinen /* Check if enough video memory. */ 326f7018c21STomi Valkeinen tmp = var->yres_virtual * var->xres_virtual * bytes_per_pixel; 327f7018c21STomi Valkeinen if (tmp > dinfo->fb.size) { 328f7018c21STomi Valkeinen WRN_MSG("Not enough video ram for mode " 329f7018c21STomi Valkeinen "(%d KByte vs %d KByte).\n", 330f7018c21STomi Valkeinen BtoKB(tmp), BtoKB(dinfo->fb.size)); 331f7018c21STomi Valkeinen return 1; 332f7018c21STomi Valkeinen } 333f7018c21STomi Valkeinen 334f7018c21STomi Valkeinen /* Check if x/y limits are OK. */ 335f7018c21STomi Valkeinen if (var->xres - 1 > HACTIVE_MASK) { 336f7018c21STomi Valkeinen WRN_MSG("X resolution too large (%d vs %d).\n", 337f7018c21STomi Valkeinen var->xres, HACTIVE_MASK + 1); 338f7018c21STomi Valkeinen return 1; 339f7018c21STomi Valkeinen } 340f7018c21STomi Valkeinen if (var->yres - 1 > VACTIVE_MASK) { 341f7018c21STomi Valkeinen WRN_MSG("Y resolution too large (%d vs %d).\n", 342f7018c21STomi Valkeinen var->yres, VACTIVE_MASK + 1); 343f7018c21STomi Valkeinen return 1; 344f7018c21STomi Valkeinen } 345f7018c21STomi Valkeinen if (var->xres < 4) { 346f7018c21STomi Valkeinen WRN_MSG("X resolution too small (%d vs 4).\n", var->xres); 347f7018c21STomi Valkeinen return 1; 348f7018c21STomi Valkeinen } 349f7018c21STomi Valkeinen if (var->yres < 4) { 350f7018c21STomi Valkeinen WRN_MSG("Y resolution too small (%d vs 4).\n", var->yres); 351f7018c21STomi Valkeinen return 1; 352f7018c21STomi Valkeinen } 353f7018c21STomi Valkeinen 354f7018c21STomi Valkeinen /* Check for doublescan modes. */ 355f7018c21STomi Valkeinen if (var->vmode & FB_VMODE_DOUBLE) { 356f7018c21STomi Valkeinen WRN_MSG("Mode is double-scan.\n"); 357f7018c21STomi Valkeinen return 1; 358f7018c21STomi Valkeinen } 359f7018c21STomi Valkeinen 360f7018c21STomi Valkeinen if ((var->vmode & FB_VMODE_INTERLACED) && (var->yres & 1)) { 361f7018c21STomi Valkeinen WRN_MSG("Odd number of lines in interlaced mode\n"); 362f7018c21STomi Valkeinen return 1; 363f7018c21STomi Valkeinen } 364f7018c21STomi Valkeinen 365f7018c21STomi Valkeinen /* Check if clock is OK. */ 366f7018c21STomi Valkeinen tmp = 1000000000 / var->pixclock; 367f7018c21STomi Valkeinen if (tmp < MIN_CLOCK) { 368f7018c21STomi Valkeinen WRN_MSG("Pixel clock is too low (%d MHz vs %d MHz).\n", 369f7018c21STomi Valkeinen (tmp + 500) / 1000, MIN_CLOCK / 1000); 370f7018c21STomi Valkeinen return 1; 371f7018c21STomi Valkeinen } 372f7018c21STomi Valkeinen if (tmp > MAX_CLOCK) { 373f7018c21STomi Valkeinen WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n", 374f7018c21STomi Valkeinen (tmp + 500) / 1000, MAX_CLOCK / 1000); 375f7018c21STomi Valkeinen return 1; 376f7018c21STomi Valkeinen } 377f7018c21STomi Valkeinen 378f7018c21STomi Valkeinen return 0; 379f7018c21STomi Valkeinen } 380f7018c21STomi Valkeinen 381f7018c21STomi Valkeinen int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 382f7018c21STomi Valkeinen { 383f7018c21STomi Valkeinen struct intelfb_info *dinfo = GET_DINFO(info); 384f7018c21STomi Valkeinen u32 offset, xoffset, yoffset; 385f7018c21STomi Valkeinen 386f7018c21STomi Valkeinen #if VERBOSE > 0 387f7018c21STomi Valkeinen DBG_MSG("intelfbhw_pan_display\n"); 388f7018c21STomi Valkeinen #endif 389f7018c21STomi Valkeinen 390f7018c21STomi Valkeinen xoffset = ROUND_DOWN_TO(var->xoffset, 8); 391f7018c21STomi Valkeinen yoffset = var->yoffset; 392f7018c21STomi Valkeinen 393f7018c21STomi Valkeinen if ((xoffset + info->var.xres > info->var.xres_virtual) || 394f7018c21STomi Valkeinen (yoffset + info->var.yres > info->var.yres_virtual)) 395f7018c21STomi Valkeinen return -EINVAL; 396f7018c21STomi Valkeinen 397f7018c21STomi Valkeinen offset = (yoffset * dinfo->pitch) + 398f7018c21STomi Valkeinen (xoffset * info->var.bits_per_pixel) / 8; 399f7018c21STomi Valkeinen 400f7018c21STomi Valkeinen offset += dinfo->fb.offset << 12; 401f7018c21STomi Valkeinen 402f7018c21STomi Valkeinen dinfo->vsync.pan_offset = offset; 403f7018c21STomi Valkeinen if ((var->activate & FB_ACTIVATE_VBL) && 404f7018c21STomi Valkeinen !intelfbhw_enable_irq(dinfo)) 405f7018c21STomi Valkeinen dinfo->vsync.pan_display = 1; 406f7018c21STomi Valkeinen else { 407f7018c21STomi Valkeinen dinfo->vsync.pan_display = 0; 408f7018c21STomi Valkeinen OUTREG(DSPABASE, offset); 409f7018c21STomi Valkeinen } 410f7018c21STomi Valkeinen 411f7018c21STomi Valkeinen return 0; 412f7018c21STomi Valkeinen } 413f7018c21STomi Valkeinen 414f7018c21STomi Valkeinen /* Blank the screen. */ 415f7018c21STomi Valkeinen void intelfbhw_do_blank(int blank, struct fb_info *info) 416f7018c21STomi Valkeinen { 417f7018c21STomi Valkeinen struct intelfb_info *dinfo = GET_DINFO(info); 418f7018c21STomi Valkeinen u32 tmp; 419f7018c21STomi Valkeinen 420f7018c21STomi Valkeinen #if VERBOSE > 0 421f7018c21STomi Valkeinen DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank); 422f7018c21STomi Valkeinen #endif 423f7018c21STomi Valkeinen 424f7018c21STomi Valkeinen /* Turn plane A on or off */ 425f7018c21STomi Valkeinen tmp = INREG(DSPACNTR); 426f7018c21STomi Valkeinen if (blank) 427f7018c21STomi Valkeinen tmp &= ~DISPPLANE_PLANE_ENABLE; 428f7018c21STomi Valkeinen else 429f7018c21STomi Valkeinen tmp |= DISPPLANE_PLANE_ENABLE; 430f7018c21STomi Valkeinen OUTREG(DSPACNTR, tmp); 431f7018c21STomi Valkeinen /* Flush */ 432f7018c21STomi Valkeinen tmp = INREG(DSPABASE); 433f7018c21STomi Valkeinen OUTREG(DSPABASE, tmp); 434f7018c21STomi Valkeinen 435f7018c21STomi Valkeinen /* Turn off/on the HW cursor */ 436f7018c21STomi Valkeinen #if VERBOSE > 0 437f7018c21STomi Valkeinen DBG_MSG("cursor_on is %d\n", dinfo->cursor_on); 438f7018c21STomi Valkeinen #endif 439f7018c21STomi Valkeinen if (dinfo->cursor_on) { 440f7018c21STomi Valkeinen if (blank) 441f7018c21STomi Valkeinen intelfbhw_cursor_hide(dinfo); 442f7018c21STomi Valkeinen else 443f7018c21STomi Valkeinen intelfbhw_cursor_show(dinfo); 444f7018c21STomi Valkeinen dinfo->cursor_on = 1; 445f7018c21STomi Valkeinen } 446f7018c21STomi Valkeinen dinfo->cursor_blanked = blank; 447f7018c21STomi Valkeinen 448f7018c21STomi Valkeinen /* Set DPMS level */ 449f7018c21STomi Valkeinen tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK; 450f7018c21STomi Valkeinen switch (blank) { 451f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 452f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 453f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D0; 454f7018c21STomi Valkeinen break; 455f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 456f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D1; 457f7018c21STomi Valkeinen break; 458f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 459f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D2; 460f7018c21STomi Valkeinen break; 461f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 462f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D3; 463f7018c21STomi Valkeinen break; 464f7018c21STomi Valkeinen } 465f7018c21STomi Valkeinen OUTREG(ADPA, tmp); 466f7018c21STomi Valkeinen 467f7018c21STomi Valkeinen return; 468f7018c21STomi Valkeinen } 469f7018c21STomi Valkeinen 470f7018c21STomi Valkeinen 471f7018c21STomi Valkeinen /* Check which pipe is connected to an active display plane. */ 472f7018c21STomi Valkeinen int intelfbhw_active_pipe(const struct intelfb_hwstate *hw) 473f7018c21STomi Valkeinen { 474f7018c21STomi Valkeinen int pipe = -1; 475f7018c21STomi Valkeinen 476f7018c21STomi Valkeinen /* keep old default behaviour - prefer PIPE_A */ 477f7018c21STomi Valkeinen if (hw->disp_b_ctrl & DISPPLANE_PLANE_ENABLE) { 478f7018c21STomi Valkeinen pipe = (hw->disp_b_ctrl >> DISPPLANE_SEL_PIPE_SHIFT); 479f7018c21STomi Valkeinen pipe &= PIPE_MASK; 480f7018c21STomi Valkeinen if (unlikely(pipe == PIPE_A)) 481f7018c21STomi Valkeinen return PIPE_A; 482f7018c21STomi Valkeinen } 483f7018c21STomi Valkeinen if (hw->disp_a_ctrl & DISPPLANE_PLANE_ENABLE) { 484f7018c21STomi Valkeinen pipe = (hw->disp_a_ctrl >> DISPPLANE_SEL_PIPE_SHIFT); 485f7018c21STomi Valkeinen pipe &= PIPE_MASK; 486f7018c21STomi Valkeinen if (likely(pipe == PIPE_A)) 487f7018c21STomi Valkeinen return PIPE_A; 488f7018c21STomi Valkeinen } 489f7018c21STomi Valkeinen /* Impossible that no pipe is selected - return PIPE_A */ 490f7018c21STomi Valkeinen WARN_ON(pipe == -1); 491f7018c21STomi Valkeinen if (unlikely(pipe == -1)) 492f7018c21STomi Valkeinen pipe = PIPE_A; 493f7018c21STomi Valkeinen 494f7018c21STomi Valkeinen return pipe; 495f7018c21STomi Valkeinen } 496f7018c21STomi Valkeinen 497f7018c21STomi Valkeinen void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, 498f7018c21STomi Valkeinen unsigned red, unsigned green, unsigned blue, 499f7018c21STomi Valkeinen unsigned transp) 500f7018c21STomi Valkeinen { 501f7018c21STomi Valkeinen u32 palette_reg = (dinfo->pipe == PIPE_A) ? 502f7018c21STomi Valkeinen PALETTE_A : PALETTE_B; 503f7018c21STomi Valkeinen 504f7018c21STomi Valkeinen #if VERBOSE > 0 505f7018c21STomi Valkeinen DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n", 506f7018c21STomi Valkeinen regno, red, green, blue); 507f7018c21STomi Valkeinen #endif 508f7018c21STomi Valkeinen 509f7018c21STomi Valkeinen OUTREG(palette_reg + (regno << 2), 510f7018c21STomi Valkeinen (red << PALETTE_8_RED_SHIFT) | 511f7018c21STomi Valkeinen (green << PALETTE_8_GREEN_SHIFT) | 512f7018c21STomi Valkeinen (blue << PALETTE_8_BLUE_SHIFT)); 513f7018c21STomi Valkeinen } 514f7018c21STomi Valkeinen 515f7018c21STomi Valkeinen 516f7018c21STomi Valkeinen int intelfbhw_read_hw_state(struct intelfb_info *dinfo, 517f7018c21STomi Valkeinen struct intelfb_hwstate *hw, int flag) 518f7018c21STomi Valkeinen { 519f7018c21STomi Valkeinen int i; 520f7018c21STomi Valkeinen 521f7018c21STomi Valkeinen #if VERBOSE > 0 522f7018c21STomi Valkeinen DBG_MSG("intelfbhw_read_hw_state\n"); 523f7018c21STomi Valkeinen #endif 524f7018c21STomi Valkeinen 525f7018c21STomi Valkeinen if (!hw || !dinfo) 526f7018c21STomi Valkeinen return -1; 527f7018c21STomi Valkeinen 528f7018c21STomi Valkeinen /* Read in as much of the HW state as possible. */ 529f7018c21STomi Valkeinen hw->vga0_divisor = INREG(VGA0_DIVISOR); 530f7018c21STomi Valkeinen hw->vga1_divisor = INREG(VGA1_DIVISOR); 531f7018c21STomi Valkeinen hw->vga_pd = INREG(VGAPD); 532f7018c21STomi Valkeinen hw->dpll_a = INREG(DPLL_A); 533f7018c21STomi Valkeinen hw->dpll_b = INREG(DPLL_B); 534f7018c21STomi Valkeinen hw->fpa0 = INREG(FPA0); 535f7018c21STomi Valkeinen hw->fpa1 = INREG(FPA1); 536f7018c21STomi Valkeinen hw->fpb0 = INREG(FPB0); 537f7018c21STomi Valkeinen hw->fpb1 = INREG(FPB1); 538f7018c21STomi Valkeinen 539f7018c21STomi Valkeinen if (flag == 1) 540f7018c21STomi Valkeinen return flag; 541f7018c21STomi Valkeinen 542f7018c21STomi Valkeinen #if 0 543f7018c21STomi Valkeinen /* This seems to be a problem with the 852GM/855GM */ 544f7018c21STomi Valkeinen for (i = 0; i < PALETTE_8_ENTRIES; i++) { 545f7018c21STomi Valkeinen hw->palette_a[i] = INREG(PALETTE_A + (i << 2)); 546f7018c21STomi Valkeinen hw->palette_b[i] = INREG(PALETTE_B + (i << 2)); 547f7018c21STomi Valkeinen } 548f7018c21STomi Valkeinen #endif 549f7018c21STomi Valkeinen 550f7018c21STomi Valkeinen if (flag == 2) 551f7018c21STomi Valkeinen return flag; 552f7018c21STomi Valkeinen 553f7018c21STomi Valkeinen hw->htotal_a = INREG(HTOTAL_A); 554f7018c21STomi Valkeinen hw->hblank_a = INREG(HBLANK_A); 555f7018c21STomi Valkeinen hw->hsync_a = INREG(HSYNC_A); 556f7018c21STomi Valkeinen hw->vtotal_a = INREG(VTOTAL_A); 557f7018c21STomi Valkeinen hw->vblank_a = INREG(VBLANK_A); 558f7018c21STomi Valkeinen hw->vsync_a = INREG(VSYNC_A); 559f7018c21STomi Valkeinen hw->src_size_a = INREG(SRC_SIZE_A); 560f7018c21STomi Valkeinen hw->bclrpat_a = INREG(BCLRPAT_A); 561f7018c21STomi Valkeinen hw->htotal_b = INREG(HTOTAL_B); 562f7018c21STomi Valkeinen hw->hblank_b = INREG(HBLANK_B); 563f7018c21STomi Valkeinen hw->hsync_b = INREG(HSYNC_B); 564f7018c21STomi Valkeinen hw->vtotal_b = INREG(VTOTAL_B); 565f7018c21STomi Valkeinen hw->vblank_b = INREG(VBLANK_B); 566f7018c21STomi Valkeinen hw->vsync_b = INREG(VSYNC_B); 567f7018c21STomi Valkeinen hw->src_size_b = INREG(SRC_SIZE_B); 568f7018c21STomi Valkeinen hw->bclrpat_b = INREG(BCLRPAT_B); 569f7018c21STomi Valkeinen 570f7018c21STomi Valkeinen if (flag == 3) 571f7018c21STomi Valkeinen return flag; 572f7018c21STomi Valkeinen 573f7018c21STomi Valkeinen hw->adpa = INREG(ADPA); 574f7018c21STomi Valkeinen hw->dvoa = INREG(DVOA); 575f7018c21STomi Valkeinen hw->dvob = INREG(DVOB); 576f7018c21STomi Valkeinen hw->dvoc = INREG(DVOC); 577f7018c21STomi Valkeinen hw->dvoa_srcdim = INREG(DVOA_SRCDIM); 578f7018c21STomi Valkeinen hw->dvob_srcdim = INREG(DVOB_SRCDIM); 579f7018c21STomi Valkeinen hw->dvoc_srcdim = INREG(DVOC_SRCDIM); 580f7018c21STomi Valkeinen hw->lvds = INREG(LVDS); 581f7018c21STomi Valkeinen 582f7018c21STomi Valkeinen if (flag == 4) 583f7018c21STomi Valkeinen return flag; 584f7018c21STomi Valkeinen 585f7018c21STomi Valkeinen hw->pipe_a_conf = INREG(PIPEACONF); 586f7018c21STomi Valkeinen hw->pipe_b_conf = INREG(PIPEBCONF); 587f7018c21STomi Valkeinen hw->disp_arb = INREG(DISPARB); 588f7018c21STomi Valkeinen 589f7018c21STomi Valkeinen if (flag == 5) 590f7018c21STomi Valkeinen return flag; 591f7018c21STomi Valkeinen 592f7018c21STomi Valkeinen hw->cursor_a_control = INREG(CURSOR_A_CONTROL); 593f7018c21STomi Valkeinen hw->cursor_b_control = INREG(CURSOR_B_CONTROL); 594f7018c21STomi Valkeinen hw->cursor_a_base = INREG(CURSOR_A_BASEADDR); 595f7018c21STomi Valkeinen hw->cursor_b_base = INREG(CURSOR_B_BASEADDR); 596f7018c21STomi Valkeinen 597f7018c21STomi Valkeinen if (flag == 6) 598f7018c21STomi Valkeinen return flag; 599f7018c21STomi Valkeinen 600f7018c21STomi Valkeinen for (i = 0; i < 4; i++) { 601f7018c21STomi Valkeinen hw->cursor_a_palette[i] = INREG(CURSOR_A_PALETTE0 + (i << 2)); 602f7018c21STomi Valkeinen hw->cursor_b_palette[i] = INREG(CURSOR_B_PALETTE0 + (i << 2)); 603f7018c21STomi Valkeinen } 604f7018c21STomi Valkeinen 605f7018c21STomi Valkeinen if (flag == 7) 606f7018c21STomi Valkeinen return flag; 607f7018c21STomi Valkeinen 608f7018c21STomi Valkeinen hw->cursor_size = INREG(CURSOR_SIZE); 609f7018c21STomi Valkeinen 610f7018c21STomi Valkeinen if (flag == 8) 611f7018c21STomi Valkeinen return flag; 612f7018c21STomi Valkeinen 613f7018c21STomi Valkeinen hw->disp_a_ctrl = INREG(DSPACNTR); 614f7018c21STomi Valkeinen hw->disp_b_ctrl = INREG(DSPBCNTR); 615f7018c21STomi Valkeinen hw->disp_a_base = INREG(DSPABASE); 616f7018c21STomi Valkeinen hw->disp_b_base = INREG(DSPBBASE); 617f7018c21STomi Valkeinen hw->disp_a_stride = INREG(DSPASTRIDE); 618f7018c21STomi Valkeinen hw->disp_b_stride = INREG(DSPBSTRIDE); 619f7018c21STomi Valkeinen 620f7018c21STomi Valkeinen if (flag == 9) 621f7018c21STomi Valkeinen return flag; 622f7018c21STomi Valkeinen 623f7018c21STomi Valkeinen hw->vgacntrl = INREG(VGACNTRL); 624f7018c21STomi Valkeinen 625f7018c21STomi Valkeinen if (flag == 10) 626f7018c21STomi Valkeinen return flag; 627f7018c21STomi Valkeinen 628f7018c21STomi Valkeinen hw->add_id = INREG(ADD_ID); 629f7018c21STomi Valkeinen 630f7018c21STomi Valkeinen if (flag == 11) 631f7018c21STomi Valkeinen return flag; 632f7018c21STomi Valkeinen 633f7018c21STomi Valkeinen for (i = 0; i < 7; i++) { 634f7018c21STomi Valkeinen hw->swf0x[i] = INREG(SWF00 + (i << 2)); 635f7018c21STomi Valkeinen hw->swf1x[i] = INREG(SWF10 + (i << 2)); 636f7018c21STomi Valkeinen if (i < 3) 637f7018c21STomi Valkeinen hw->swf3x[i] = INREG(SWF30 + (i << 2)); 638f7018c21STomi Valkeinen } 639f7018c21STomi Valkeinen 640f7018c21STomi Valkeinen for (i = 0; i < 8; i++) 641f7018c21STomi Valkeinen hw->fence[i] = INREG(FENCE + (i << 2)); 642f7018c21STomi Valkeinen 643f7018c21STomi Valkeinen hw->instpm = INREG(INSTPM); 644f7018c21STomi Valkeinen hw->mem_mode = INREG(MEM_MODE); 645f7018c21STomi Valkeinen hw->fw_blc_0 = INREG(FW_BLC_0); 646f7018c21STomi Valkeinen hw->fw_blc_1 = INREG(FW_BLC_1); 647f7018c21STomi Valkeinen 648f7018c21STomi Valkeinen hw->hwstam = INREG16(HWSTAM); 649f7018c21STomi Valkeinen hw->ier = INREG16(IER); 650f7018c21STomi Valkeinen hw->iir = INREG16(IIR); 651f7018c21STomi Valkeinen hw->imr = INREG16(IMR); 652f7018c21STomi Valkeinen 653f7018c21STomi Valkeinen return 0; 654f7018c21STomi Valkeinen } 655f7018c21STomi Valkeinen 656f7018c21STomi Valkeinen 657f7018c21STomi Valkeinen static int calc_vclock3(int index, int m, int n, int p) 658f7018c21STomi Valkeinen { 659f7018c21STomi Valkeinen if (p == 0 || n == 0) 660f7018c21STomi Valkeinen return 0; 661f7018c21STomi Valkeinen return plls[index].ref_clk * m / n / p; 662f7018c21STomi Valkeinen } 663f7018c21STomi Valkeinen 664f7018c21STomi Valkeinen static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, 665f7018c21STomi Valkeinen int lvds) 666f7018c21STomi Valkeinen { 667f7018c21STomi Valkeinen struct pll_min_max *pll = &plls[index]; 668f7018c21STomi Valkeinen u32 m, vco, p; 669f7018c21STomi Valkeinen 670f7018c21STomi Valkeinen m = (5 * (m1 + 2)) + (m2 + 2); 671f7018c21STomi Valkeinen n += 2; 672f7018c21STomi Valkeinen vco = pll->ref_clk * m / n; 673f7018c21STomi Valkeinen 674f7018c21STomi Valkeinen if (index == PLLS_I8xx) 675f7018c21STomi Valkeinen p = ((p1 + 2) * (1 << (p2 + 1))); 676f7018c21STomi Valkeinen else 677f7018c21STomi Valkeinen p = ((p1) * (p2 ? 5 : 10)); 678f7018c21STomi Valkeinen return vco / p; 679f7018c21STomi Valkeinen } 680f7018c21STomi Valkeinen 681f7018c21STomi Valkeinen #if REGDUMP 682f7018c21STomi Valkeinen static void intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, 683f7018c21STomi Valkeinen int *o_p1, int *o_p2) 684f7018c21STomi Valkeinen { 685f7018c21STomi Valkeinen int p1, p2; 686f7018c21STomi Valkeinen 687f7018c21STomi Valkeinen if (IS_I9XX(dinfo)) { 688f7018c21STomi Valkeinen if (dpll & DPLL_P1_FORCE_DIV2) 689f7018c21STomi Valkeinen p1 = 1; 690f7018c21STomi Valkeinen else 691f7018c21STomi Valkeinen p1 = (dpll >> DPLL_P1_SHIFT) & 0xff; 692f7018c21STomi Valkeinen 693f7018c21STomi Valkeinen p1 = ffs(p1); 694f7018c21STomi Valkeinen 695f7018c21STomi Valkeinen p2 = (dpll >> DPLL_I9XX_P2_SHIFT) & DPLL_P2_MASK; 696f7018c21STomi Valkeinen } else { 697f7018c21STomi Valkeinen if (dpll & DPLL_P1_FORCE_DIV2) 698f7018c21STomi Valkeinen p1 = 0; 699f7018c21STomi Valkeinen else 700f7018c21STomi Valkeinen p1 = (dpll >> DPLL_P1_SHIFT) & DPLL_P1_MASK; 701f7018c21STomi Valkeinen p2 = (dpll >> DPLL_P2_SHIFT) & DPLL_P2_MASK; 702f7018c21STomi Valkeinen } 703f7018c21STomi Valkeinen 704f7018c21STomi Valkeinen *o_p1 = p1; 705f7018c21STomi Valkeinen *o_p2 = p2; 706f7018c21STomi Valkeinen } 707f7018c21STomi Valkeinen #endif 708f7018c21STomi Valkeinen 709f7018c21STomi Valkeinen 710f7018c21STomi Valkeinen void intelfbhw_print_hw_state(struct intelfb_info *dinfo, 711f7018c21STomi Valkeinen struct intelfb_hwstate *hw) 712f7018c21STomi Valkeinen { 713f7018c21STomi Valkeinen #if REGDUMP 714f7018c21STomi Valkeinen int i, m1, m2, n, p1, p2; 715f7018c21STomi Valkeinen int index = dinfo->pll_index; 716f7018c21STomi Valkeinen DBG_MSG("intelfbhw_print_hw_state\n"); 717f7018c21STomi Valkeinen 718f7018c21STomi Valkeinen if (!hw) 719f7018c21STomi Valkeinen return; 720f7018c21STomi Valkeinen /* Read in as much of the HW state as possible. */ 721f7018c21STomi Valkeinen printk("hw state dump start\n"); 722f7018c21STomi Valkeinen printk(" VGA0_DIVISOR: 0x%08x\n", hw->vga0_divisor); 723f7018c21STomi Valkeinen printk(" VGA1_DIVISOR: 0x%08x\n", hw->vga1_divisor); 724f7018c21STomi Valkeinen printk(" VGAPD: 0x%08x\n", hw->vga_pd); 725f7018c21STomi Valkeinen n = (hw->vga0_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 726f7018c21STomi Valkeinen m1 = (hw->vga0_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 727f7018c21STomi Valkeinen m2 = (hw->vga0_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 728f7018c21STomi Valkeinen 729f7018c21STomi Valkeinen intelfbhw_get_p1p2(dinfo, hw->vga_pd, &p1, &p2); 730f7018c21STomi Valkeinen 731f7018c21STomi Valkeinen printk(" VGA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", 732f7018c21STomi Valkeinen m1, m2, n, p1, p2); 733f7018c21STomi Valkeinen printk(" VGA0: clock is %d\n", 734f7018c21STomi Valkeinen calc_vclock(index, m1, m2, n, p1, p2, 0)); 735f7018c21STomi Valkeinen 736f7018c21STomi Valkeinen n = (hw->vga1_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 737f7018c21STomi Valkeinen m1 = (hw->vga1_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 738f7018c21STomi Valkeinen m2 = (hw->vga1_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 739f7018c21STomi Valkeinen 740f7018c21STomi Valkeinen intelfbhw_get_p1p2(dinfo, hw->vga_pd, &p1, &p2); 741f7018c21STomi Valkeinen printk(" VGA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", 742f7018c21STomi Valkeinen m1, m2, n, p1, p2); 743f7018c21STomi Valkeinen printk(" VGA1: clock is %d\n", 744f7018c21STomi Valkeinen calc_vclock(index, m1, m2, n, p1, p2, 0)); 745f7018c21STomi Valkeinen 746f7018c21STomi Valkeinen printk(" DPLL_A: 0x%08x\n", hw->dpll_a); 747f7018c21STomi Valkeinen printk(" DPLL_B: 0x%08x\n", hw->dpll_b); 748f7018c21STomi Valkeinen printk(" FPA0: 0x%08x\n", hw->fpa0); 749f7018c21STomi Valkeinen printk(" FPA1: 0x%08x\n", hw->fpa1); 750f7018c21STomi Valkeinen printk(" FPB0: 0x%08x\n", hw->fpb0); 751f7018c21STomi Valkeinen printk(" FPB1: 0x%08x\n", hw->fpb1); 752f7018c21STomi Valkeinen 753f7018c21STomi Valkeinen n = (hw->fpa0 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 754f7018c21STomi Valkeinen m1 = (hw->fpa0 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 755f7018c21STomi Valkeinen m2 = (hw->fpa0 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 756f7018c21STomi Valkeinen 757f7018c21STomi Valkeinen intelfbhw_get_p1p2(dinfo, hw->dpll_a, &p1, &p2); 758f7018c21STomi Valkeinen 759f7018c21STomi Valkeinen printk(" PLLA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", 760f7018c21STomi Valkeinen m1, m2, n, p1, p2); 761f7018c21STomi Valkeinen printk(" PLLA0: clock is %d\n", 762f7018c21STomi Valkeinen calc_vclock(index, m1, m2, n, p1, p2, 0)); 763f7018c21STomi Valkeinen 764f7018c21STomi Valkeinen n = (hw->fpa1 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 765f7018c21STomi Valkeinen m1 = (hw->fpa1 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 766f7018c21STomi Valkeinen m2 = (hw->fpa1 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; 767f7018c21STomi Valkeinen 768f7018c21STomi Valkeinen intelfbhw_get_p1p2(dinfo, hw->dpll_a, &p1, &p2); 769f7018c21STomi Valkeinen 770f7018c21STomi Valkeinen printk(" PLLA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", 771f7018c21STomi Valkeinen m1, m2, n, p1, p2); 772f7018c21STomi Valkeinen printk(" PLLA1: clock is %d\n", 773f7018c21STomi Valkeinen calc_vclock(index, m1, m2, n, p1, p2, 0)); 774f7018c21STomi Valkeinen 775f7018c21STomi Valkeinen #if 0 776f7018c21STomi Valkeinen printk(" PALETTE_A:\n"); 777f7018c21STomi Valkeinen for (i = 0; i < PALETTE_8_ENTRIES) 778f7018c21STomi Valkeinen printk(" %3d: 0x%08x\n", i, hw->palette_a[i]); 779f7018c21STomi Valkeinen printk(" PALETTE_B:\n"); 780f7018c21STomi Valkeinen for (i = 0; i < PALETTE_8_ENTRIES) 781f7018c21STomi Valkeinen printk(" %3d: 0x%08x\n", i, hw->palette_b[i]); 782f7018c21STomi Valkeinen #endif 783f7018c21STomi Valkeinen 784f7018c21STomi Valkeinen printk(" HTOTAL_A: 0x%08x\n", hw->htotal_a); 785f7018c21STomi Valkeinen printk(" HBLANK_A: 0x%08x\n", hw->hblank_a); 786f7018c21STomi Valkeinen printk(" HSYNC_A: 0x%08x\n", hw->hsync_a); 787f7018c21STomi Valkeinen printk(" VTOTAL_A: 0x%08x\n", hw->vtotal_a); 788f7018c21STomi Valkeinen printk(" VBLANK_A: 0x%08x\n", hw->vblank_a); 789f7018c21STomi Valkeinen printk(" VSYNC_A: 0x%08x\n", hw->vsync_a); 790f7018c21STomi Valkeinen printk(" SRC_SIZE_A: 0x%08x\n", hw->src_size_a); 791f7018c21STomi Valkeinen printk(" BCLRPAT_A: 0x%08x\n", hw->bclrpat_a); 792f7018c21STomi Valkeinen printk(" HTOTAL_B: 0x%08x\n", hw->htotal_b); 793f7018c21STomi Valkeinen printk(" HBLANK_B: 0x%08x\n", hw->hblank_b); 794f7018c21STomi Valkeinen printk(" HSYNC_B: 0x%08x\n", hw->hsync_b); 795f7018c21STomi Valkeinen printk(" VTOTAL_B: 0x%08x\n", hw->vtotal_b); 796f7018c21STomi Valkeinen printk(" VBLANK_B: 0x%08x\n", hw->vblank_b); 797f7018c21STomi Valkeinen printk(" VSYNC_B: 0x%08x\n", hw->vsync_b); 798f7018c21STomi Valkeinen printk(" SRC_SIZE_B: 0x%08x\n", hw->src_size_b); 799f7018c21STomi Valkeinen printk(" BCLRPAT_B: 0x%08x\n", hw->bclrpat_b); 800f7018c21STomi Valkeinen 801f7018c21STomi Valkeinen printk(" ADPA: 0x%08x\n", hw->adpa); 802f7018c21STomi Valkeinen printk(" DVOA: 0x%08x\n", hw->dvoa); 803f7018c21STomi Valkeinen printk(" DVOB: 0x%08x\n", hw->dvob); 804f7018c21STomi Valkeinen printk(" DVOC: 0x%08x\n", hw->dvoc); 805f7018c21STomi Valkeinen printk(" DVOA_SRCDIM: 0x%08x\n", hw->dvoa_srcdim); 806f7018c21STomi Valkeinen printk(" DVOB_SRCDIM: 0x%08x\n", hw->dvob_srcdim); 807f7018c21STomi Valkeinen printk(" DVOC_SRCDIM: 0x%08x\n", hw->dvoc_srcdim); 808f7018c21STomi Valkeinen printk(" LVDS: 0x%08x\n", hw->lvds); 809f7018c21STomi Valkeinen 810f7018c21STomi Valkeinen printk(" PIPEACONF: 0x%08x\n", hw->pipe_a_conf); 811f7018c21STomi Valkeinen printk(" PIPEBCONF: 0x%08x\n", hw->pipe_b_conf); 812f7018c21STomi Valkeinen printk(" DISPARB: 0x%08x\n", hw->disp_arb); 813f7018c21STomi Valkeinen 814f7018c21STomi Valkeinen printk(" CURSOR_A_CONTROL: 0x%08x\n", hw->cursor_a_control); 815f7018c21STomi Valkeinen printk(" CURSOR_B_CONTROL: 0x%08x\n", hw->cursor_b_control); 816f7018c21STomi Valkeinen printk(" CURSOR_A_BASEADDR: 0x%08x\n", hw->cursor_a_base); 817f7018c21STomi Valkeinen printk(" CURSOR_B_BASEADDR: 0x%08x\n", hw->cursor_b_base); 818f7018c21STomi Valkeinen 819f7018c21STomi Valkeinen printk(" CURSOR_A_PALETTE: "); 820f7018c21STomi Valkeinen for (i = 0; i < 4; i++) { 821f7018c21STomi Valkeinen printk("0x%08x", hw->cursor_a_palette[i]); 822f7018c21STomi Valkeinen if (i < 3) 823f7018c21STomi Valkeinen printk(", "); 824f7018c21STomi Valkeinen } 825f7018c21STomi Valkeinen printk("\n"); 826f7018c21STomi Valkeinen printk(" CURSOR_B_PALETTE: "); 827f7018c21STomi Valkeinen for (i = 0; i < 4; i++) { 828f7018c21STomi Valkeinen printk("0x%08x", hw->cursor_b_palette[i]); 829f7018c21STomi Valkeinen if (i < 3) 830f7018c21STomi Valkeinen printk(", "); 831f7018c21STomi Valkeinen } 832f7018c21STomi Valkeinen printk("\n"); 833f7018c21STomi Valkeinen 834f7018c21STomi Valkeinen printk(" CURSOR_SIZE: 0x%08x\n", hw->cursor_size); 835f7018c21STomi Valkeinen 836f7018c21STomi Valkeinen printk(" DSPACNTR: 0x%08x\n", hw->disp_a_ctrl); 837f7018c21STomi Valkeinen printk(" DSPBCNTR: 0x%08x\n", hw->disp_b_ctrl); 838f7018c21STomi Valkeinen printk(" DSPABASE: 0x%08x\n", hw->disp_a_base); 839f7018c21STomi Valkeinen printk(" DSPBBASE: 0x%08x\n", hw->disp_b_base); 840f7018c21STomi Valkeinen printk(" DSPASTRIDE: 0x%08x\n", hw->disp_a_stride); 841f7018c21STomi Valkeinen printk(" DSPBSTRIDE: 0x%08x\n", hw->disp_b_stride); 842f7018c21STomi Valkeinen 843f7018c21STomi Valkeinen printk(" VGACNTRL: 0x%08x\n", hw->vgacntrl); 844f7018c21STomi Valkeinen printk(" ADD_ID: 0x%08x\n", hw->add_id); 845f7018c21STomi Valkeinen 846f7018c21STomi Valkeinen for (i = 0; i < 7; i++) { 847f7018c21STomi Valkeinen printk(" SWF0%d 0x%08x\n", i, 848f7018c21STomi Valkeinen hw->swf0x[i]); 849f7018c21STomi Valkeinen } 850f7018c21STomi Valkeinen for (i = 0; i < 7; i++) { 851f7018c21STomi Valkeinen printk(" SWF1%d 0x%08x\n", i, 852f7018c21STomi Valkeinen hw->swf1x[i]); 853f7018c21STomi Valkeinen } 854f7018c21STomi Valkeinen for (i = 0; i < 3; i++) { 855f7018c21STomi Valkeinen printk(" SWF3%d 0x%08x\n", i, 856f7018c21STomi Valkeinen hw->swf3x[i]); 857f7018c21STomi Valkeinen } 858f7018c21STomi Valkeinen for (i = 0; i < 8; i++) 859f7018c21STomi Valkeinen printk(" FENCE%d 0x%08x\n", i, 860f7018c21STomi Valkeinen hw->fence[i]); 861f7018c21STomi Valkeinen 862f7018c21STomi Valkeinen printk(" INSTPM 0x%08x\n", hw->instpm); 863f7018c21STomi Valkeinen printk(" MEM_MODE 0x%08x\n", hw->mem_mode); 864f7018c21STomi Valkeinen printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); 865f7018c21STomi Valkeinen printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); 866f7018c21STomi Valkeinen 867f7018c21STomi Valkeinen printk(" HWSTAM 0x%04x\n", hw->hwstam); 868f7018c21STomi Valkeinen printk(" IER 0x%04x\n", hw->ier); 869f7018c21STomi Valkeinen printk(" IIR 0x%04x\n", hw->iir); 870f7018c21STomi Valkeinen printk(" IMR 0x%04x\n", hw->imr); 871f7018c21STomi Valkeinen printk("hw state dump end\n"); 872f7018c21STomi Valkeinen #endif 873f7018c21STomi Valkeinen } 874f7018c21STomi Valkeinen 875f7018c21STomi Valkeinen 876f7018c21STomi Valkeinen 877f7018c21STomi Valkeinen /* Split the M parameter into M1 and M2. */ 878f7018c21STomi Valkeinen static int splitm(int index, unsigned int m, unsigned int *retm1, 879f7018c21STomi Valkeinen unsigned int *retm2) 880f7018c21STomi Valkeinen { 881f7018c21STomi Valkeinen int m1, m2; 882f7018c21STomi Valkeinen int testm; 883f7018c21STomi Valkeinen struct pll_min_max *pll = &plls[index]; 884f7018c21STomi Valkeinen 885f7018c21STomi Valkeinen /* no point optimising too much - brute force m */ 886f7018c21STomi Valkeinen for (m1 = pll->min_m1; m1 < pll->max_m1 + 1; m1++) { 887f7018c21STomi Valkeinen for (m2 = pll->min_m2; m2 < pll->max_m2 + 1; m2++) { 888f7018c21STomi Valkeinen testm = (5 * (m1 + 2)) + (m2 + 2); 889f7018c21STomi Valkeinen if (testm == m) { 890f7018c21STomi Valkeinen *retm1 = (unsigned int)m1; 891f7018c21STomi Valkeinen *retm2 = (unsigned int)m2; 892f7018c21STomi Valkeinen return 0; 893f7018c21STomi Valkeinen } 894f7018c21STomi Valkeinen } 895f7018c21STomi Valkeinen } 896f7018c21STomi Valkeinen return 1; 897f7018c21STomi Valkeinen } 898f7018c21STomi Valkeinen 899f7018c21STomi Valkeinen /* Split the P parameter into P1 and P2. */ 900f7018c21STomi Valkeinen static int splitp(int index, unsigned int p, unsigned int *retp1, 901f7018c21STomi Valkeinen unsigned int *retp2) 902f7018c21STomi Valkeinen { 903f7018c21STomi Valkeinen int p1, p2; 904f7018c21STomi Valkeinen struct pll_min_max *pll = &plls[index]; 905f7018c21STomi Valkeinen 906f7018c21STomi Valkeinen if (index == PLLS_I9xx) { 907f7018c21STomi Valkeinen p2 = (p % 10) ? 1 : 0; 908f7018c21STomi Valkeinen 909f7018c21STomi Valkeinen p1 = p / (p2 ? 5 : 10); 910f7018c21STomi Valkeinen 911f7018c21STomi Valkeinen *retp1 = (unsigned int)p1; 912f7018c21STomi Valkeinen *retp2 = (unsigned int)p2; 913f7018c21STomi Valkeinen return 0; 914f7018c21STomi Valkeinen } 915f7018c21STomi Valkeinen 916f7018c21STomi Valkeinen if (p % 4 == 0) 917f7018c21STomi Valkeinen p2 = 1; 918f7018c21STomi Valkeinen else 919f7018c21STomi Valkeinen p2 = 0; 920f7018c21STomi Valkeinen p1 = (p / (1 << (p2 + 1))) - 2; 921f7018c21STomi Valkeinen if (p % 4 == 0 && p1 < pll->min_p1) { 922f7018c21STomi Valkeinen p2 = 0; 923f7018c21STomi Valkeinen p1 = (p / (1 << (p2 + 1))) - 2; 924f7018c21STomi Valkeinen } 925f7018c21STomi Valkeinen if (p1 < pll->min_p1 || p1 > pll->max_p1 || 926f7018c21STomi Valkeinen (p1 + 2) * (1 << (p2 + 1)) != p) { 927f7018c21STomi Valkeinen return 1; 928f7018c21STomi Valkeinen } else { 929f7018c21STomi Valkeinen *retp1 = (unsigned int)p1; 930f7018c21STomi Valkeinen *retp2 = (unsigned int)p2; 931f7018c21STomi Valkeinen return 0; 932f7018c21STomi Valkeinen } 933f7018c21STomi Valkeinen } 934f7018c21STomi Valkeinen 935f7018c21STomi Valkeinen static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, 936f7018c21STomi Valkeinen u32 *retn, u32 *retp1, u32 *retp2, u32 *retclock) 937f7018c21STomi Valkeinen { 938f7018c21STomi Valkeinen u32 m1, m2, n, p1, p2, n1, testm; 939f7018c21STomi Valkeinen u32 f_vco, p, p_best = 0, m, f_out = 0; 940f7018c21STomi Valkeinen u32 err_max, err_target, err_best = 10000000; 941f7018c21STomi Valkeinen u32 n_best = 0, m_best = 0, f_best, f_err; 942f7018c21STomi Valkeinen u32 p_min, p_max, p_inc, div_max; 943f7018c21STomi Valkeinen struct pll_min_max *pll = &plls[index]; 944f7018c21STomi Valkeinen 945f7018c21STomi Valkeinen /* Accept 0.5% difference, but aim for 0.1% */ 946f7018c21STomi Valkeinen err_max = 5 * clock / 1000; 947f7018c21STomi Valkeinen err_target = clock / 1000; 948f7018c21STomi Valkeinen 949f7018c21STomi Valkeinen DBG_MSG("Clock is %d\n", clock); 950f7018c21STomi Valkeinen 951f7018c21STomi Valkeinen div_max = pll->max_vco / clock; 952f7018c21STomi Valkeinen 953f7018c21STomi Valkeinen p_inc = (clock <= pll->p_transition_clk) ? pll->p_inc_lo : pll->p_inc_hi; 954f7018c21STomi Valkeinen p_min = p_inc; 955f7018c21STomi Valkeinen p_max = ROUND_DOWN_TO(div_max, p_inc); 956f7018c21STomi Valkeinen if (p_min < pll->min_p) 957f7018c21STomi Valkeinen p_min = pll->min_p; 958f7018c21STomi Valkeinen if (p_max > pll->max_p) 959f7018c21STomi Valkeinen p_max = pll->max_p; 960f7018c21STomi Valkeinen 961f7018c21STomi Valkeinen DBG_MSG("p range is %d-%d (%d)\n", p_min, p_max, p_inc); 962f7018c21STomi Valkeinen 963f7018c21STomi Valkeinen p = p_min; 964f7018c21STomi Valkeinen do { 965f7018c21STomi Valkeinen if (splitp(index, p, &p1, &p2)) { 966f7018c21STomi Valkeinen WRN_MSG("cannot split p = %d\n", p); 967f7018c21STomi Valkeinen p += p_inc; 968f7018c21STomi Valkeinen continue; 969f7018c21STomi Valkeinen } 970f7018c21STomi Valkeinen n = pll->min_n; 971f7018c21STomi Valkeinen f_vco = clock * p; 972f7018c21STomi Valkeinen 973f7018c21STomi Valkeinen do { 974f7018c21STomi Valkeinen m = ROUND_UP_TO(f_vco * n, pll->ref_clk) / pll->ref_clk; 975f7018c21STomi Valkeinen if (m < pll->min_m) 976f7018c21STomi Valkeinen m = pll->min_m + 1; 977f7018c21STomi Valkeinen if (m > pll->max_m) 978f7018c21STomi Valkeinen m = pll->max_m - 1; 979f7018c21STomi Valkeinen for (testm = m - 1; testm <= m; testm++) { 980f7018c21STomi Valkeinen f_out = calc_vclock3(index, testm, n, p); 981f7018c21STomi Valkeinen if (splitm(index, testm, &m1, &m2)) { 982f7018c21STomi Valkeinen WRN_MSG("cannot split m = %d\n", 983f7018c21STomi Valkeinen testm); 984f7018c21STomi Valkeinen continue; 985f7018c21STomi Valkeinen } 986f7018c21STomi Valkeinen if (clock > f_out) 987f7018c21STomi Valkeinen f_err = clock - f_out; 988f7018c21STomi Valkeinen else/* slightly bias the error for bigger clocks */ 989f7018c21STomi Valkeinen f_err = f_out - clock + 1; 990f7018c21STomi Valkeinen 991f7018c21STomi Valkeinen if (f_err < err_best) { 992f7018c21STomi Valkeinen m_best = testm; 993f7018c21STomi Valkeinen n_best = n; 994f7018c21STomi Valkeinen p_best = p; 995f7018c21STomi Valkeinen f_best = f_out; 996f7018c21STomi Valkeinen err_best = f_err; 997f7018c21STomi Valkeinen } 998f7018c21STomi Valkeinen } 999f7018c21STomi Valkeinen n++; 1000f7018c21STomi Valkeinen } while ((n <= pll->max_n) && (f_out >= clock)); 1001f7018c21STomi Valkeinen p += p_inc; 1002f7018c21STomi Valkeinen } while ((p <= p_max)); 1003f7018c21STomi Valkeinen 1004f7018c21STomi Valkeinen if (!m_best) { 1005f7018c21STomi Valkeinen WRN_MSG("cannot find parameters for clock %d\n", clock); 1006f7018c21STomi Valkeinen return 1; 1007f7018c21STomi Valkeinen } 1008f7018c21STomi Valkeinen m = m_best; 1009f7018c21STomi Valkeinen n = n_best; 1010f7018c21STomi Valkeinen p = p_best; 1011f7018c21STomi Valkeinen splitm(index, m, &m1, &m2); 1012f7018c21STomi Valkeinen splitp(index, p, &p1, &p2); 1013f7018c21STomi Valkeinen n1 = n - 2; 1014f7018c21STomi Valkeinen 1015f7018c21STomi Valkeinen DBG_MSG("m, n, p: %d (%d,%d), %d (%d), %d (%d,%d), " 1016f7018c21STomi Valkeinen "f: %d (%d), VCO: %d\n", 1017f7018c21STomi Valkeinen m, m1, m2, n, n1, p, p1, p2, 1018f7018c21STomi Valkeinen calc_vclock3(index, m, n, p), 1019f7018c21STomi Valkeinen calc_vclock(index, m1, m2, n1, p1, p2, 0), 1020f7018c21STomi Valkeinen calc_vclock3(index, m, n, p) * p); 1021f7018c21STomi Valkeinen *retm1 = m1; 1022f7018c21STomi Valkeinen *retm2 = m2; 1023f7018c21STomi Valkeinen *retn = n1; 1024f7018c21STomi Valkeinen *retp1 = p1; 1025f7018c21STomi Valkeinen *retp2 = p2; 1026f7018c21STomi Valkeinen *retclock = calc_vclock(index, m1, m2, n1, p1, p2, 0); 1027f7018c21STomi Valkeinen 1028f7018c21STomi Valkeinen return 0; 1029f7018c21STomi Valkeinen } 1030f7018c21STomi Valkeinen 1031f7018c21STomi Valkeinen static __inline__ int check_overflow(u32 value, u32 limit, 1032f7018c21STomi Valkeinen const char *description) 1033f7018c21STomi Valkeinen { 1034f7018c21STomi Valkeinen if (value > limit) { 1035f7018c21STomi Valkeinen WRN_MSG("%s value %d exceeds limit %d\n", 1036f7018c21STomi Valkeinen description, value, limit); 1037f7018c21STomi Valkeinen return 1; 1038f7018c21STomi Valkeinen } 1039f7018c21STomi Valkeinen return 0; 1040f7018c21STomi Valkeinen } 1041f7018c21STomi Valkeinen 1042f7018c21STomi Valkeinen /* It is assumed that hw is filled in with the initial state information. */ 1043f7018c21STomi Valkeinen int intelfbhw_mode_to_hw(struct intelfb_info *dinfo, 1044f7018c21STomi Valkeinen struct intelfb_hwstate *hw, 1045f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 1046f7018c21STomi Valkeinen { 1047f7018c21STomi Valkeinen int pipe = intelfbhw_active_pipe(hw); 1048f7018c21STomi Valkeinen u32 *dpll, *fp0, *fp1; 1049f7018c21STomi Valkeinen u32 m1, m2, n, p1, p2, clock_target, clock; 1050f7018c21STomi Valkeinen u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive; 1051f7018c21STomi Valkeinen u32 vsync_start, vsync_end, vblank_start, vblank_end, vtotal, vactive; 1052f7018c21STomi Valkeinen u32 vsync_pol, hsync_pol; 1053f7018c21STomi Valkeinen u32 *vs, *vb, *vt, *hs, *hb, *ht, *ss, *pipe_conf; 1054f7018c21STomi Valkeinen u32 stride_alignment; 1055f7018c21STomi Valkeinen 1056f7018c21STomi Valkeinen DBG_MSG("intelfbhw_mode_to_hw\n"); 1057f7018c21STomi Valkeinen 1058f7018c21STomi Valkeinen /* Disable VGA */ 1059f7018c21STomi Valkeinen hw->vgacntrl |= VGA_DISABLE; 1060f7018c21STomi Valkeinen 1061f7018c21STomi Valkeinen /* Set which pipe's registers will be set. */ 1062f7018c21STomi Valkeinen if (pipe == PIPE_B) { 1063f7018c21STomi Valkeinen dpll = &hw->dpll_b; 1064f7018c21STomi Valkeinen fp0 = &hw->fpb0; 1065f7018c21STomi Valkeinen fp1 = &hw->fpb1; 1066f7018c21STomi Valkeinen hs = &hw->hsync_b; 1067f7018c21STomi Valkeinen hb = &hw->hblank_b; 1068f7018c21STomi Valkeinen ht = &hw->htotal_b; 1069f7018c21STomi Valkeinen vs = &hw->vsync_b; 1070f7018c21STomi Valkeinen vb = &hw->vblank_b; 1071f7018c21STomi Valkeinen vt = &hw->vtotal_b; 1072f7018c21STomi Valkeinen ss = &hw->src_size_b; 1073f7018c21STomi Valkeinen pipe_conf = &hw->pipe_b_conf; 1074f7018c21STomi Valkeinen } else { 1075f7018c21STomi Valkeinen dpll = &hw->dpll_a; 1076f7018c21STomi Valkeinen fp0 = &hw->fpa0; 1077f7018c21STomi Valkeinen fp1 = &hw->fpa1; 1078f7018c21STomi Valkeinen hs = &hw->hsync_a; 1079f7018c21STomi Valkeinen hb = &hw->hblank_a; 1080f7018c21STomi Valkeinen ht = &hw->htotal_a; 1081f7018c21STomi Valkeinen vs = &hw->vsync_a; 1082f7018c21STomi Valkeinen vb = &hw->vblank_a; 1083f7018c21STomi Valkeinen vt = &hw->vtotal_a; 1084f7018c21STomi Valkeinen ss = &hw->src_size_a; 1085f7018c21STomi Valkeinen pipe_conf = &hw->pipe_a_conf; 1086f7018c21STomi Valkeinen } 1087f7018c21STomi Valkeinen 1088f7018c21STomi Valkeinen /* Use ADPA register for sync control. */ 1089f7018c21STomi Valkeinen hw->adpa &= ~ADPA_USE_VGA_HVPOLARITY; 1090f7018c21STomi Valkeinen 1091f7018c21STomi Valkeinen /* sync polarity */ 1092f7018c21STomi Valkeinen hsync_pol = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 1093f7018c21STomi Valkeinen ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW; 1094f7018c21STomi Valkeinen vsync_pol = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 1095f7018c21STomi Valkeinen ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW; 1096f7018c21STomi Valkeinen hw->adpa &= ~((ADPA_SYNC_ACTIVE_MASK << ADPA_VSYNC_ACTIVE_SHIFT) | 1097f7018c21STomi Valkeinen (ADPA_SYNC_ACTIVE_MASK << ADPA_HSYNC_ACTIVE_SHIFT)); 1098f7018c21STomi Valkeinen hw->adpa |= (hsync_pol << ADPA_HSYNC_ACTIVE_SHIFT) | 1099f7018c21STomi Valkeinen (vsync_pol << ADPA_VSYNC_ACTIVE_SHIFT); 1100f7018c21STomi Valkeinen 1101f7018c21STomi Valkeinen /* Connect correct pipe to the analog port DAC */ 1102f7018c21STomi Valkeinen hw->adpa &= ~(PIPE_MASK << ADPA_PIPE_SELECT_SHIFT); 1103f7018c21STomi Valkeinen hw->adpa |= (pipe << ADPA_PIPE_SELECT_SHIFT); 1104f7018c21STomi Valkeinen 1105f7018c21STomi Valkeinen /* Set DPMS state to D0 (on) */ 1106f7018c21STomi Valkeinen hw->adpa &= ~ADPA_DPMS_CONTROL_MASK; 1107f7018c21STomi Valkeinen hw->adpa |= ADPA_DPMS_D0; 1108f7018c21STomi Valkeinen 1109f7018c21STomi Valkeinen hw->adpa |= ADPA_DAC_ENABLE; 1110f7018c21STomi Valkeinen 1111f7018c21STomi Valkeinen *dpll |= (DPLL_VCO_ENABLE | DPLL_VGA_MODE_DISABLE); 1112f7018c21STomi Valkeinen *dpll &= ~(DPLL_RATE_SELECT_MASK | DPLL_REFERENCE_SELECT_MASK); 1113f7018c21STomi Valkeinen *dpll |= (DPLL_REFERENCE_DEFAULT | DPLL_RATE_SELECT_FP0); 1114f7018c21STomi Valkeinen 1115f7018c21STomi Valkeinen /* Desired clock in kHz */ 1116f7018c21STomi Valkeinen clock_target = 1000000000 / var->pixclock; 1117f7018c21STomi Valkeinen 1118f7018c21STomi Valkeinen if (calc_pll_params(dinfo->pll_index, clock_target, &m1, &m2, 1119f7018c21STomi Valkeinen &n, &p1, &p2, &clock)) { 1120f7018c21STomi Valkeinen WRN_MSG("calc_pll_params failed\n"); 1121f7018c21STomi Valkeinen return 1; 1122f7018c21STomi Valkeinen } 1123f7018c21STomi Valkeinen 1124f7018c21STomi Valkeinen /* Check for overflow. */ 1125f7018c21STomi Valkeinen if (check_overflow(p1, DPLL_P1_MASK, "PLL P1 parameter")) 1126f7018c21STomi Valkeinen return 1; 1127f7018c21STomi Valkeinen if (check_overflow(p2, DPLL_P2_MASK, "PLL P2 parameter")) 1128f7018c21STomi Valkeinen return 1; 1129f7018c21STomi Valkeinen if (check_overflow(m1, FP_DIVISOR_MASK, "PLL M1 parameter")) 1130f7018c21STomi Valkeinen return 1; 1131f7018c21STomi Valkeinen if (check_overflow(m2, FP_DIVISOR_MASK, "PLL M2 parameter")) 1132f7018c21STomi Valkeinen return 1; 1133f7018c21STomi Valkeinen if (check_overflow(n, FP_DIVISOR_MASK, "PLL N parameter")) 1134f7018c21STomi Valkeinen return 1; 1135f7018c21STomi Valkeinen 1136f7018c21STomi Valkeinen *dpll &= ~DPLL_P1_FORCE_DIV2; 1137f7018c21STomi Valkeinen *dpll &= ~((DPLL_P2_MASK << DPLL_P2_SHIFT) | 1138f7018c21STomi Valkeinen (DPLL_P1_MASK << DPLL_P1_SHIFT)); 1139f7018c21STomi Valkeinen 1140f7018c21STomi Valkeinen if (IS_I9XX(dinfo)) { 1141f7018c21STomi Valkeinen *dpll |= (p2 << DPLL_I9XX_P2_SHIFT); 1142f7018c21STomi Valkeinen *dpll |= (1 << (p1 - 1)) << DPLL_P1_SHIFT; 1143f7018c21STomi Valkeinen } else 1144f7018c21STomi Valkeinen *dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT); 1145f7018c21STomi Valkeinen 1146f7018c21STomi Valkeinen *fp0 = (n << FP_N_DIVISOR_SHIFT) | 1147f7018c21STomi Valkeinen (m1 << FP_M1_DIVISOR_SHIFT) | 1148f7018c21STomi Valkeinen (m2 << FP_M2_DIVISOR_SHIFT); 1149f7018c21STomi Valkeinen *fp1 = *fp0; 1150f7018c21STomi Valkeinen 1151f7018c21STomi Valkeinen hw->dvob &= ~PORT_ENABLE; 1152f7018c21STomi Valkeinen hw->dvoc &= ~PORT_ENABLE; 1153f7018c21STomi Valkeinen 1154f7018c21STomi Valkeinen /* Use display plane A. */ 1155f7018c21STomi Valkeinen hw->disp_a_ctrl |= DISPPLANE_PLANE_ENABLE; 1156f7018c21STomi Valkeinen hw->disp_a_ctrl &= ~DISPPLANE_GAMMA_ENABLE; 1157f7018c21STomi Valkeinen hw->disp_a_ctrl &= ~DISPPLANE_PIXFORMAT_MASK; 1158f7018c21STomi Valkeinen switch (intelfb_var_to_depth(var)) { 1159f7018c21STomi Valkeinen case 8: 1160f7018c21STomi Valkeinen hw->disp_a_ctrl |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE; 1161f7018c21STomi Valkeinen break; 1162f7018c21STomi Valkeinen case 15: 1163f7018c21STomi Valkeinen hw->disp_a_ctrl |= DISPPLANE_15_16BPP; 1164f7018c21STomi Valkeinen break; 1165f7018c21STomi Valkeinen case 16: 1166f7018c21STomi Valkeinen hw->disp_a_ctrl |= DISPPLANE_16BPP; 1167f7018c21STomi Valkeinen break; 1168f7018c21STomi Valkeinen case 24: 1169f7018c21STomi Valkeinen hw->disp_a_ctrl |= DISPPLANE_32BPP_NO_ALPHA; 1170f7018c21STomi Valkeinen break; 1171f7018c21STomi Valkeinen } 1172f7018c21STomi Valkeinen hw->disp_a_ctrl &= ~(PIPE_MASK << DISPPLANE_SEL_PIPE_SHIFT); 1173f7018c21STomi Valkeinen hw->disp_a_ctrl |= (pipe << DISPPLANE_SEL_PIPE_SHIFT); 1174f7018c21STomi Valkeinen 1175f7018c21STomi Valkeinen /* Set CRTC registers. */ 1176f7018c21STomi Valkeinen hactive = var->xres; 1177f7018c21STomi Valkeinen hsync_start = hactive + var->right_margin; 1178f7018c21STomi Valkeinen hsync_end = hsync_start + var->hsync_len; 1179f7018c21STomi Valkeinen htotal = hsync_end + var->left_margin; 1180f7018c21STomi Valkeinen hblank_start = hactive; 1181f7018c21STomi Valkeinen hblank_end = htotal; 1182f7018c21STomi Valkeinen 1183f7018c21STomi Valkeinen DBG_MSG("H: act %d, ss %d, se %d, tot %d bs %d, be %d\n", 1184f7018c21STomi Valkeinen hactive, hsync_start, hsync_end, htotal, hblank_start, 1185f7018c21STomi Valkeinen hblank_end); 1186f7018c21STomi Valkeinen 1187f7018c21STomi Valkeinen vactive = var->yres; 1188f7018c21STomi Valkeinen if (var->vmode & FB_VMODE_INTERLACED) 1189f7018c21STomi Valkeinen vactive--; /* the chip adds 2 halflines automatically */ 1190f7018c21STomi Valkeinen vsync_start = vactive + var->lower_margin; 1191f7018c21STomi Valkeinen vsync_end = vsync_start + var->vsync_len; 1192f7018c21STomi Valkeinen vtotal = vsync_end + var->upper_margin; 1193f7018c21STomi Valkeinen vblank_start = vactive; 1194f7018c21STomi Valkeinen vblank_end = vsync_end + 1; 1195f7018c21STomi Valkeinen 1196f7018c21STomi Valkeinen DBG_MSG("V: act %d, ss %d, se %d, tot %d bs %d, be %d\n", 1197f7018c21STomi Valkeinen vactive, vsync_start, vsync_end, vtotal, vblank_start, 1198f7018c21STomi Valkeinen vblank_end); 1199f7018c21STomi Valkeinen 1200f7018c21STomi Valkeinen /* Adjust for register values, and check for overflow. */ 1201f7018c21STomi Valkeinen hactive--; 1202f7018c21STomi Valkeinen if (check_overflow(hactive, HACTIVE_MASK, "CRTC hactive")) 1203f7018c21STomi Valkeinen return 1; 1204f7018c21STomi Valkeinen hsync_start--; 1205f7018c21STomi Valkeinen if (check_overflow(hsync_start, HSYNCSTART_MASK, "CRTC hsync_start")) 1206f7018c21STomi Valkeinen return 1; 1207f7018c21STomi Valkeinen hsync_end--; 1208f7018c21STomi Valkeinen if (check_overflow(hsync_end, HSYNCEND_MASK, "CRTC hsync_end")) 1209f7018c21STomi Valkeinen return 1; 1210f7018c21STomi Valkeinen htotal--; 1211f7018c21STomi Valkeinen if (check_overflow(htotal, HTOTAL_MASK, "CRTC htotal")) 1212f7018c21STomi Valkeinen return 1; 1213f7018c21STomi Valkeinen hblank_start--; 1214f7018c21STomi Valkeinen if (check_overflow(hblank_start, HBLANKSTART_MASK, "CRTC hblank_start")) 1215f7018c21STomi Valkeinen return 1; 1216f7018c21STomi Valkeinen hblank_end--; 1217f7018c21STomi Valkeinen if (check_overflow(hblank_end, HBLANKEND_MASK, "CRTC hblank_end")) 1218f7018c21STomi Valkeinen return 1; 1219f7018c21STomi Valkeinen 1220f7018c21STomi Valkeinen vactive--; 1221f7018c21STomi Valkeinen if (check_overflow(vactive, VACTIVE_MASK, "CRTC vactive")) 1222f7018c21STomi Valkeinen return 1; 1223f7018c21STomi Valkeinen vsync_start--; 1224f7018c21STomi Valkeinen if (check_overflow(vsync_start, VSYNCSTART_MASK, "CRTC vsync_start")) 1225f7018c21STomi Valkeinen return 1; 1226f7018c21STomi Valkeinen vsync_end--; 1227f7018c21STomi Valkeinen if (check_overflow(vsync_end, VSYNCEND_MASK, "CRTC vsync_end")) 1228f7018c21STomi Valkeinen return 1; 1229f7018c21STomi Valkeinen vtotal--; 1230f7018c21STomi Valkeinen if (check_overflow(vtotal, VTOTAL_MASK, "CRTC vtotal")) 1231f7018c21STomi Valkeinen return 1; 1232f7018c21STomi Valkeinen vblank_start--; 1233f7018c21STomi Valkeinen if (check_overflow(vblank_start, VBLANKSTART_MASK, "CRTC vblank_start")) 1234f7018c21STomi Valkeinen return 1; 1235f7018c21STomi Valkeinen vblank_end--; 1236f7018c21STomi Valkeinen if (check_overflow(vblank_end, VBLANKEND_MASK, "CRTC vblank_end")) 1237f7018c21STomi Valkeinen return 1; 1238f7018c21STomi Valkeinen 1239f7018c21STomi Valkeinen *ht = (htotal << HTOTAL_SHIFT) | (hactive << HACTIVE_SHIFT); 1240f7018c21STomi Valkeinen *hb = (hblank_start << HBLANKSTART_SHIFT) | 1241f7018c21STomi Valkeinen (hblank_end << HSYNCEND_SHIFT); 1242f7018c21STomi Valkeinen *hs = (hsync_start << HSYNCSTART_SHIFT) | (hsync_end << HSYNCEND_SHIFT); 1243f7018c21STomi Valkeinen 1244f7018c21STomi Valkeinen *vt = (vtotal << VTOTAL_SHIFT) | (vactive << VACTIVE_SHIFT); 1245f7018c21STomi Valkeinen *vb = (vblank_start << VBLANKSTART_SHIFT) | 1246f7018c21STomi Valkeinen (vblank_end << VSYNCEND_SHIFT); 1247f7018c21STomi Valkeinen *vs = (vsync_start << VSYNCSTART_SHIFT) | (vsync_end << VSYNCEND_SHIFT); 1248f7018c21STomi Valkeinen *ss = (hactive << SRC_SIZE_HORIZ_SHIFT) | 1249f7018c21STomi Valkeinen (vactive << SRC_SIZE_VERT_SHIFT); 1250f7018c21STomi Valkeinen 1251f7018c21STomi Valkeinen hw->disp_a_stride = dinfo->pitch; 1252f7018c21STomi Valkeinen DBG_MSG("pitch is %d\n", hw->disp_a_stride); 1253f7018c21STomi Valkeinen 1254f7018c21STomi Valkeinen hw->disp_a_base = hw->disp_a_stride * var->yoffset + 1255f7018c21STomi Valkeinen var->xoffset * var->bits_per_pixel / 8; 1256f7018c21STomi Valkeinen 1257f7018c21STomi Valkeinen hw->disp_a_base += dinfo->fb.offset << 12; 1258f7018c21STomi Valkeinen 1259f7018c21STomi Valkeinen /* Check stride alignment. */ 1260f7018c21STomi Valkeinen stride_alignment = IS_I9XX(dinfo) ? STRIDE_ALIGNMENT_I9XX : 1261f7018c21STomi Valkeinen STRIDE_ALIGNMENT; 1262f7018c21STomi Valkeinen if (hw->disp_a_stride % stride_alignment != 0) { 1263f7018c21STomi Valkeinen WRN_MSG("display stride %d has bad alignment %d\n", 1264f7018c21STomi Valkeinen hw->disp_a_stride, stride_alignment); 1265f7018c21STomi Valkeinen return 1; 1266f7018c21STomi Valkeinen } 1267f7018c21STomi Valkeinen 1268f7018c21STomi Valkeinen /* Set the palette to 8-bit mode. */ 1269f7018c21STomi Valkeinen *pipe_conf &= ~PIPECONF_GAMMA; 1270f7018c21STomi Valkeinen 1271f7018c21STomi Valkeinen if (var->vmode & FB_VMODE_INTERLACED) 1272f7018c21STomi Valkeinen *pipe_conf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; 1273f7018c21STomi Valkeinen else 1274f7018c21STomi Valkeinen *pipe_conf &= ~PIPECONF_INTERLACE_MASK; 1275f7018c21STomi Valkeinen 1276f7018c21STomi Valkeinen return 0; 1277f7018c21STomi Valkeinen } 1278f7018c21STomi Valkeinen 1279f7018c21STomi Valkeinen /* Program a (non-VGA) video mode. */ 1280f7018c21STomi Valkeinen int intelfbhw_program_mode(struct intelfb_info *dinfo, 1281f7018c21STomi Valkeinen const struct intelfb_hwstate *hw, int blank) 1282f7018c21STomi Valkeinen { 1283f7018c21STomi Valkeinen u32 tmp; 1284f7018c21STomi Valkeinen const u32 *dpll, *fp0, *fp1, *pipe_conf; 1285f7018c21STomi Valkeinen const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss; 1286f7018c21STomi Valkeinen u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg, pipe_stat_reg; 1287f7018c21STomi Valkeinen u32 hsync_reg, htotal_reg, hblank_reg; 1288f7018c21STomi Valkeinen u32 vsync_reg, vtotal_reg, vblank_reg; 1289f7018c21STomi Valkeinen u32 src_size_reg; 1290f7018c21STomi Valkeinen u32 count, tmp_val[3]; 1291f7018c21STomi Valkeinen 1292f7018c21STomi Valkeinen /* Assume single pipe */ 1293f7018c21STomi Valkeinen 1294f7018c21STomi Valkeinen #if VERBOSE > 0 1295f7018c21STomi Valkeinen DBG_MSG("intelfbhw_program_mode\n"); 1296f7018c21STomi Valkeinen #endif 1297f7018c21STomi Valkeinen 1298f7018c21STomi Valkeinen /* Disable VGA */ 1299f7018c21STomi Valkeinen tmp = INREG(VGACNTRL); 1300f7018c21STomi Valkeinen tmp |= VGA_DISABLE; 1301f7018c21STomi Valkeinen OUTREG(VGACNTRL, tmp); 1302f7018c21STomi Valkeinen 1303f7018c21STomi Valkeinen dinfo->pipe = intelfbhw_active_pipe(hw); 1304f7018c21STomi Valkeinen 1305f7018c21STomi Valkeinen if (dinfo->pipe == PIPE_B) { 1306f7018c21STomi Valkeinen dpll = &hw->dpll_b; 1307f7018c21STomi Valkeinen fp0 = &hw->fpb0; 1308f7018c21STomi Valkeinen fp1 = &hw->fpb1; 1309f7018c21STomi Valkeinen pipe_conf = &hw->pipe_b_conf; 1310f7018c21STomi Valkeinen hs = &hw->hsync_b; 1311f7018c21STomi Valkeinen hb = &hw->hblank_b; 1312f7018c21STomi Valkeinen ht = &hw->htotal_b; 1313f7018c21STomi Valkeinen vs = &hw->vsync_b; 1314f7018c21STomi Valkeinen vb = &hw->vblank_b; 1315f7018c21STomi Valkeinen vt = &hw->vtotal_b; 1316f7018c21STomi Valkeinen ss = &hw->src_size_b; 1317f7018c21STomi Valkeinen dpll_reg = DPLL_B; 1318f7018c21STomi Valkeinen fp0_reg = FPB0; 1319f7018c21STomi Valkeinen fp1_reg = FPB1; 1320f7018c21STomi Valkeinen pipe_conf_reg = PIPEBCONF; 1321f7018c21STomi Valkeinen pipe_stat_reg = PIPEBSTAT; 1322f7018c21STomi Valkeinen hsync_reg = HSYNC_B; 1323f7018c21STomi Valkeinen htotal_reg = HTOTAL_B; 1324f7018c21STomi Valkeinen hblank_reg = HBLANK_B; 1325f7018c21STomi Valkeinen vsync_reg = VSYNC_B; 1326f7018c21STomi Valkeinen vtotal_reg = VTOTAL_B; 1327f7018c21STomi Valkeinen vblank_reg = VBLANK_B; 1328f7018c21STomi Valkeinen src_size_reg = SRC_SIZE_B; 1329f7018c21STomi Valkeinen } else { 1330f7018c21STomi Valkeinen dpll = &hw->dpll_a; 1331f7018c21STomi Valkeinen fp0 = &hw->fpa0; 1332f7018c21STomi Valkeinen fp1 = &hw->fpa1; 1333f7018c21STomi Valkeinen pipe_conf = &hw->pipe_a_conf; 1334f7018c21STomi Valkeinen hs = &hw->hsync_a; 1335f7018c21STomi Valkeinen hb = &hw->hblank_a; 1336f7018c21STomi Valkeinen ht = &hw->htotal_a; 1337f7018c21STomi Valkeinen vs = &hw->vsync_a; 1338f7018c21STomi Valkeinen vb = &hw->vblank_a; 1339f7018c21STomi Valkeinen vt = &hw->vtotal_a; 1340f7018c21STomi Valkeinen ss = &hw->src_size_a; 1341f7018c21STomi Valkeinen dpll_reg = DPLL_A; 1342f7018c21STomi Valkeinen fp0_reg = FPA0; 1343f7018c21STomi Valkeinen fp1_reg = FPA1; 1344f7018c21STomi Valkeinen pipe_conf_reg = PIPEACONF; 1345f7018c21STomi Valkeinen pipe_stat_reg = PIPEASTAT; 1346f7018c21STomi Valkeinen hsync_reg = HSYNC_A; 1347f7018c21STomi Valkeinen htotal_reg = HTOTAL_A; 1348f7018c21STomi Valkeinen hblank_reg = HBLANK_A; 1349f7018c21STomi Valkeinen vsync_reg = VSYNC_A; 1350f7018c21STomi Valkeinen vtotal_reg = VTOTAL_A; 1351f7018c21STomi Valkeinen vblank_reg = VBLANK_A; 1352f7018c21STomi Valkeinen src_size_reg = SRC_SIZE_A; 1353f7018c21STomi Valkeinen } 1354f7018c21STomi Valkeinen 1355f7018c21STomi Valkeinen /* turn off pipe */ 1356f7018c21STomi Valkeinen tmp = INREG(pipe_conf_reg); 1357f7018c21STomi Valkeinen tmp &= ~PIPECONF_ENABLE; 1358f7018c21STomi Valkeinen OUTREG(pipe_conf_reg, tmp); 1359f7018c21STomi Valkeinen 1360f7018c21STomi Valkeinen count = 0; 1361f7018c21STomi Valkeinen do { 1362f7018c21STomi Valkeinen tmp_val[count % 3] = INREG(PIPEA_DSL); 1363f7018c21STomi Valkeinen if ((tmp_val[0] == tmp_val[1]) && (tmp_val[1] == tmp_val[2])) 1364f7018c21STomi Valkeinen break; 1365f7018c21STomi Valkeinen count++; 1366f7018c21STomi Valkeinen udelay(1); 1367f7018c21STomi Valkeinen if (count % 200 == 0) { 1368f7018c21STomi Valkeinen tmp = INREG(pipe_conf_reg); 1369f7018c21STomi Valkeinen tmp &= ~PIPECONF_ENABLE; 1370f7018c21STomi Valkeinen OUTREG(pipe_conf_reg, tmp); 1371f7018c21STomi Valkeinen } 1372f7018c21STomi Valkeinen } while (count < 2000); 1373f7018c21STomi Valkeinen 1374f7018c21STomi Valkeinen OUTREG(ADPA, INREG(ADPA) & ~ADPA_DAC_ENABLE); 1375f7018c21STomi Valkeinen 1376f7018c21STomi Valkeinen /* Disable planes A and B. */ 1377f7018c21STomi Valkeinen tmp = INREG(DSPACNTR); 1378f7018c21STomi Valkeinen tmp &= ~DISPPLANE_PLANE_ENABLE; 1379f7018c21STomi Valkeinen OUTREG(DSPACNTR, tmp); 1380f7018c21STomi Valkeinen tmp = INREG(DSPBCNTR); 1381f7018c21STomi Valkeinen tmp &= ~DISPPLANE_PLANE_ENABLE; 1382f7018c21STomi Valkeinen OUTREG(DSPBCNTR, tmp); 1383f7018c21STomi Valkeinen 1384f7018c21STomi Valkeinen /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ 1385f7018c21STomi Valkeinen mdelay(20); 1386f7018c21STomi Valkeinen 1387f7018c21STomi Valkeinen OUTREG(DVOB, INREG(DVOB) & ~PORT_ENABLE); 1388f7018c21STomi Valkeinen OUTREG(DVOC, INREG(DVOC) & ~PORT_ENABLE); 1389f7018c21STomi Valkeinen OUTREG(ADPA, INREG(ADPA) & ~ADPA_DAC_ENABLE); 1390f7018c21STomi Valkeinen 1391f7018c21STomi Valkeinen /* Disable Sync */ 1392f7018c21STomi Valkeinen tmp = INREG(ADPA); 1393f7018c21STomi Valkeinen tmp &= ~ADPA_DPMS_CONTROL_MASK; 1394f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D3; 1395f7018c21STomi Valkeinen OUTREG(ADPA, tmp); 1396f7018c21STomi Valkeinen 1397f7018c21STomi Valkeinen /* do some funky magic - xyzzy */ 1398f7018c21STomi Valkeinen OUTREG(0x61204, 0xabcd0000); 1399f7018c21STomi Valkeinen 1400f7018c21STomi Valkeinen /* turn off PLL */ 1401f7018c21STomi Valkeinen tmp = INREG(dpll_reg); 1402f7018c21STomi Valkeinen tmp &= ~DPLL_VCO_ENABLE; 1403f7018c21STomi Valkeinen OUTREG(dpll_reg, tmp); 1404f7018c21STomi Valkeinen 1405f7018c21STomi Valkeinen /* Set PLL parameters */ 1406f7018c21STomi Valkeinen OUTREG(fp0_reg, *fp0); 1407f7018c21STomi Valkeinen OUTREG(fp1_reg, *fp1); 1408f7018c21STomi Valkeinen 1409f7018c21STomi Valkeinen /* Enable PLL */ 1410f7018c21STomi Valkeinen OUTREG(dpll_reg, *dpll); 1411f7018c21STomi Valkeinen 1412f7018c21STomi Valkeinen /* Set DVOs B/C */ 1413f7018c21STomi Valkeinen OUTREG(DVOB, hw->dvob); 1414f7018c21STomi Valkeinen OUTREG(DVOC, hw->dvoc); 1415f7018c21STomi Valkeinen 1416f7018c21STomi Valkeinen /* undo funky magic */ 1417f7018c21STomi Valkeinen OUTREG(0x61204, 0x00000000); 1418f7018c21STomi Valkeinen 1419f7018c21STomi Valkeinen /* Set ADPA */ 1420f7018c21STomi Valkeinen OUTREG(ADPA, INREG(ADPA) | ADPA_DAC_ENABLE); 1421f7018c21STomi Valkeinen OUTREG(ADPA, (hw->adpa & ~(ADPA_DPMS_CONTROL_MASK)) | ADPA_DPMS_D3); 1422f7018c21STomi Valkeinen 1423f7018c21STomi Valkeinen /* Set pipe parameters */ 1424f7018c21STomi Valkeinen OUTREG(hsync_reg, *hs); 1425f7018c21STomi Valkeinen OUTREG(hblank_reg, *hb); 1426f7018c21STomi Valkeinen OUTREG(htotal_reg, *ht); 1427f7018c21STomi Valkeinen OUTREG(vsync_reg, *vs); 1428f7018c21STomi Valkeinen OUTREG(vblank_reg, *vb); 1429f7018c21STomi Valkeinen OUTREG(vtotal_reg, *vt); 1430f7018c21STomi Valkeinen OUTREG(src_size_reg, *ss); 1431f7018c21STomi Valkeinen 1432f7018c21STomi Valkeinen switch (dinfo->info->var.vmode & (FB_VMODE_INTERLACED | 1433f7018c21STomi Valkeinen FB_VMODE_ODD_FLD_FIRST)) { 1434f7018c21STomi Valkeinen case FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST: 1435f7018c21STomi Valkeinen OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_ODD_EN); 1436f7018c21STomi Valkeinen break; 1437f7018c21STomi Valkeinen case FB_VMODE_INTERLACED: /* even lines first */ 1438f7018c21STomi Valkeinen OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_EVEN_EN); 1439f7018c21STomi Valkeinen break; 1440f7018c21STomi Valkeinen default: /* non-interlaced */ 1441f7018c21STomi Valkeinen OUTREG(pipe_stat_reg, 0xFFFF); /* clear all status bits only */ 1442f7018c21STomi Valkeinen } 1443f7018c21STomi Valkeinen /* Enable pipe */ 1444f7018c21STomi Valkeinen OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE); 1445f7018c21STomi Valkeinen 1446f7018c21STomi Valkeinen /* Enable sync */ 1447f7018c21STomi Valkeinen tmp = INREG(ADPA); 1448f7018c21STomi Valkeinen tmp &= ~ADPA_DPMS_CONTROL_MASK; 1449f7018c21STomi Valkeinen tmp |= ADPA_DPMS_D0; 1450f7018c21STomi Valkeinen OUTREG(ADPA, tmp); 1451f7018c21STomi Valkeinen 1452f7018c21STomi Valkeinen /* setup display plane */ 1453f7018c21STomi Valkeinen if (dinfo->pdev->device == PCI_DEVICE_ID_INTEL_830M) { 1454f7018c21STomi Valkeinen /* 1455f7018c21STomi Valkeinen * i830M errata: the display plane must be enabled 1456f7018c21STomi Valkeinen * to allow writes to the other bits in the plane 1457f7018c21STomi Valkeinen * control register. 1458f7018c21STomi Valkeinen */ 1459f7018c21STomi Valkeinen tmp = INREG(DSPACNTR); 1460f7018c21STomi Valkeinen if ((tmp & DISPPLANE_PLANE_ENABLE) != DISPPLANE_PLANE_ENABLE) { 1461f7018c21STomi Valkeinen tmp |= DISPPLANE_PLANE_ENABLE; 1462f7018c21STomi Valkeinen OUTREG(DSPACNTR, tmp); 1463f7018c21STomi Valkeinen OUTREG(DSPACNTR, 1464f7018c21STomi Valkeinen hw->disp_a_ctrl|DISPPLANE_PLANE_ENABLE); 1465f7018c21STomi Valkeinen mdelay(1); 1466f7018c21STomi Valkeinen } 1467f7018c21STomi Valkeinen } 1468f7018c21STomi Valkeinen 1469f7018c21STomi Valkeinen OUTREG(DSPACNTR, hw->disp_a_ctrl & ~DISPPLANE_PLANE_ENABLE); 1470f7018c21STomi Valkeinen OUTREG(DSPASTRIDE, hw->disp_a_stride); 1471f7018c21STomi Valkeinen OUTREG(DSPABASE, hw->disp_a_base); 1472f7018c21STomi Valkeinen 1473f7018c21STomi Valkeinen /* Enable plane */ 1474f7018c21STomi Valkeinen if (!blank) { 1475f7018c21STomi Valkeinen tmp = INREG(DSPACNTR); 1476f7018c21STomi Valkeinen tmp |= DISPPLANE_PLANE_ENABLE; 1477f7018c21STomi Valkeinen OUTREG(DSPACNTR, tmp); 1478f7018c21STomi Valkeinen OUTREG(DSPABASE, hw->disp_a_base); 1479f7018c21STomi Valkeinen } 1480f7018c21STomi Valkeinen 1481f7018c21STomi Valkeinen return 0; 1482f7018c21STomi Valkeinen } 1483f7018c21STomi Valkeinen 1484f7018c21STomi Valkeinen /* forward declarations */ 1485f7018c21STomi Valkeinen static void refresh_ring(struct intelfb_info *dinfo); 1486f7018c21STomi Valkeinen static void reset_state(struct intelfb_info *dinfo); 1487f7018c21STomi Valkeinen static void do_flush(struct intelfb_info *dinfo); 1488f7018c21STomi Valkeinen 1489f7018c21STomi Valkeinen static u32 get_ring_space(struct intelfb_info *dinfo) 1490f7018c21STomi Valkeinen { 1491f7018c21STomi Valkeinen u32 ring_space; 1492f7018c21STomi Valkeinen 1493f7018c21STomi Valkeinen if (dinfo->ring_tail >= dinfo->ring_head) 1494f7018c21STomi Valkeinen ring_space = dinfo->ring.size - 1495f7018c21STomi Valkeinen (dinfo->ring_tail - dinfo->ring_head); 1496f7018c21STomi Valkeinen else 1497f7018c21STomi Valkeinen ring_space = dinfo->ring_head - dinfo->ring_tail; 1498f7018c21STomi Valkeinen 1499f7018c21STomi Valkeinen if (ring_space > RING_MIN_FREE) 1500f7018c21STomi Valkeinen ring_space -= RING_MIN_FREE; 1501f7018c21STomi Valkeinen else 1502f7018c21STomi Valkeinen ring_space = 0; 1503f7018c21STomi Valkeinen 1504f7018c21STomi Valkeinen return ring_space; 1505f7018c21STomi Valkeinen } 1506f7018c21STomi Valkeinen 1507f7018c21STomi Valkeinen static int wait_ring(struct intelfb_info *dinfo, int n) 1508f7018c21STomi Valkeinen { 1509f7018c21STomi Valkeinen int i = 0; 1510f7018c21STomi Valkeinen unsigned long end; 1511f7018c21STomi Valkeinen u32 last_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; 1512f7018c21STomi Valkeinen 1513f7018c21STomi Valkeinen #if VERBOSE > 0 1514f7018c21STomi Valkeinen DBG_MSG("wait_ring: %d\n", n); 1515f7018c21STomi Valkeinen #endif 1516f7018c21STomi Valkeinen 1517f7018c21STomi Valkeinen end = jiffies + (HZ * 3); 1518f7018c21STomi Valkeinen while (dinfo->ring_space < n) { 1519f7018c21STomi Valkeinen dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; 1520f7018c21STomi Valkeinen dinfo->ring_space = get_ring_space(dinfo); 1521f7018c21STomi Valkeinen 1522f7018c21STomi Valkeinen if (dinfo->ring_head != last_head) { 1523f7018c21STomi Valkeinen end = jiffies + (HZ * 3); 1524f7018c21STomi Valkeinen last_head = dinfo->ring_head; 1525f7018c21STomi Valkeinen } 1526f7018c21STomi Valkeinen i++; 1527f7018c21STomi Valkeinen if (time_before(end, jiffies)) { 1528f7018c21STomi Valkeinen if (!i) { 1529f7018c21STomi Valkeinen /* Try again */ 1530f7018c21STomi Valkeinen reset_state(dinfo); 1531f7018c21STomi Valkeinen refresh_ring(dinfo); 1532f7018c21STomi Valkeinen do_flush(dinfo); 1533f7018c21STomi Valkeinen end = jiffies + (HZ * 3); 1534f7018c21STomi Valkeinen i = 1; 1535f7018c21STomi Valkeinen } else { 1536f7018c21STomi Valkeinen WRN_MSG("ring buffer : space: %d wanted %d\n", 1537f7018c21STomi Valkeinen dinfo->ring_space, n); 1538f7018c21STomi Valkeinen WRN_MSG("lockup - turning off hardware " 1539f7018c21STomi Valkeinen "acceleration\n"); 1540f7018c21STomi Valkeinen dinfo->ring_lockup = 1; 1541f7018c21STomi Valkeinen break; 1542f7018c21STomi Valkeinen } 1543f7018c21STomi Valkeinen } 1544f7018c21STomi Valkeinen udelay(1); 1545f7018c21STomi Valkeinen } 1546f7018c21STomi Valkeinen return i; 1547f7018c21STomi Valkeinen } 1548f7018c21STomi Valkeinen 1549f7018c21STomi Valkeinen static void do_flush(struct intelfb_info *dinfo) 1550f7018c21STomi Valkeinen { 1551f7018c21STomi Valkeinen START_RING(2); 1552f7018c21STomi Valkeinen OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE); 1553f7018c21STomi Valkeinen OUT_RING(MI_NOOP); 1554f7018c21STomi Valkeinen ADVANCE_RING(); 1555f7018c21STomi Valkeinen } 1556f7018c21STomi Valkeinen 1557f7018c21STomi Valkeinen void intelfbhw_do_sync(struct intelfb_info *dinfo) 1558f7018c21STomi Valkeinen { 1559f7018c21STomi Valkeinen #if VERBOSE > 0 1560f7018c21STomi Valkeinen DBG_MSG("intelfbhw_do_sync\n"); 1561f7018c21STomi Valkeinen #endif 1562f7018c21STomi Valkeinen 1563f7018c21STomi Valkeinen if (!dinfo->accel) 1564f7018c21STomi Valkeinen return; 1565f7018c21STomi Valkeinen 1566f7018c21STomi Valkeinen /* 1567f7018c21STomi Valkeinen * Send a flush, then wait until the ring is empty. This is what 1568f7018c21STomi Valkeinen * the XFree86 driver does, and actually it doesn't seem a lot worse 1569f7018c21STomi Valkeinen * than the recommended method (both have problems). 1570f7018c21STomi Valkeinen */ 1571f7018c21STomi Valkeinen do_flush(dinfo); 1572f7018c21STomi Valkeinen wait_ring(dinfo, dinfo->ring.size - RING_MIN_FREE); 1573f7018c21STomi Valkeinen dinfo->ring_space = dinfo->ring.size - RING_MIN_FREE; 1574f7018c21STomi Valkeinen } 1575f7018c21STomi Valkeinen 1576f7018c21STomi Valkeinen static void refresh_ring(struct intelfb_info *dinfo) 1577f7018c21STomi Valkeinen { 1578f7018c21STomi Valkeinen #if VERBOSE > 0 1579f7018c21STomi Valkeinen DBG_MSG("refresh_ring\n"); 1580f7018c21STomi Valkeinen #endif 1581f7018c21STomi Valkeinen 1582f7018c21STomi Valkeinen dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; 1583f7018c21STomi Valkeinen dinfo->ring_tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK; 1584f7018c21STomi Valkeinen dinfo->ring_space = get_ring_space(dinfo); 1585f7018c21STomi Valkeinen } 1586f7018c21STomi Valkeinen 1587f7018c21STomi Valkeinen static void reset_state(struct intelfb_info *dinfo) 1588f7018c21STomi Valkeinen { 1589f7018c21STomi Valkeinen int i; 1590f7018c21STomi Valkeinen u32 tmp; 1591f7018c21STomi Valkeinen 1592f7018c21STomi Valkeinen #if VERBOSE > 0 1593f7018c21STomi Valkeinen DBG_MSG("reset_state\n"); 1594f7018c21STomi Valkeinen #endif 1595f7018c21STomi Valkeinen 1596f7018c21STomi Valkeinen for (i = 0; i < FENCE_NUM; i++) 1597f7018c21STomi Valkeinen OUTREG(FENCE + (i << 2), 0); 1598f7018c21STomi Valkeinen 1599f7018c21STomi Valkeinen /* Flush the ring buffer if it's enabled. */ 1600f7018c21STomi Valkeinen tmp = INREG(PRI_RING_LENGTH); 1601f7018c21STomi Valkeinen if (tmp & RING_ENABLE) { 1602f7018c21STomi Valkeinen #if VERBOSE > 0 1603f7018c21STomi Valkeinen DBG_MSG("reset_state: ring was enabled\n"); 1604f7018c21STomi Valkeinen #endif 1605f7018c21STomi Valkeinen refresh_ring(dinfo); 1606f7018c21STomi Valkeinen intelfbhw_do_sync(dinfo); 1607f7018c21STomi Valkeinen DO_RING_IDLE(); 1608f7018c21STomi Valkeinen } 1609f7018c21STomi Valkeinen 1610f7018c21STomi Valkeinen OUTREG(PRI_RING_LENGTH, 0); 1611f7018c21STomi Valkeinen OUTREG(PRI_RING_HEAD, 0); 1612f7018c21STomi Valkeinen OUTREG(PRI_RING_TAIL, 0); 1613f7018c21STomi Valkeinen OUTREG(PRI_RING_START, 0); 1614f7018c21STomi Valkeinen } 1615f7018c21STomi Valkeinen 1616f7018c21STomi Valkeinen /* Stop the 2D engine, and turn off the ring buffer. */ 1617f7018c21STomi Valkeinen void intelfbhw_2d_stop(struct intelfb_info *dinfo) 1618f7018c21STomi Valkeinen { 1619f7018c21STomi Valkeinen #if VERBOSE > 0 1620f7018c21STomi Valkeinen DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", 1621f7018c21STomi Valkeinen dinfo->accel, dinfo->ring_active); 1622f7018c21STomi Valkeinen #endif 1623f7018c21STomi Valkeinen 1624f7018c21STomi Valkeinen if (!dinfo->accel) 1625f7018c21STomi Valkeinen return; 1626f7018c21STomi Valkeinen 1627f7018c21STomi Valkeinen dinfo->ring_active = 0; 1628f7018c21STomi Valkeinen reset_state(dinfo); 1629f7018c21STomi Valkeinen } 1630f7018c21STomi Valkeinen 1631f7018c21STomi Valkeinen /* 1632f7018c21STomi Valkeinen * Enable the ring buffer, and initialise the 2D engine. 1633f7018c21STomi Valkeinen * It is assumed that the graphics engine has been stopped by previously 1634f7018c21STomi Valkeinen * calling intelfb_2d_stop(). 1635f7018c21STomi Valkeinen */ 1636f7018c21STomi Valkeinen void intelfbhw_2d_start(struct intelfb_info *dinfo) 1637f7018c21STomi Valkeinen { 1638f7018c21STomi Valkeinen #if VERBOSE > 0 1639f7018c21STomi Valkeinen DBG_MSG("intelfbhw_2d_start: accel: %d, ring_active: %d\n", 1640f7018c21STomi Valkeinen dinfo->accel, dinfo->ring_active); 1641f7018c21STomi Valkeinen #endif 1642f7018c21STomi Valkeinen 1643f7018c21STomi Valkeinen if (!dinfo->accel) 1644f7018c21STomi Valkeinen return; 1645f7018c21STomi Valkeinen 1646f7018c21STomi Valkeinen /* Initialise the primary ring buffer. */ 1647f7018c21STomi Valkeinen OUTREG(PRI_RING_LENGTH, 0); 1648f7018c21STomi Valkeinen OUTREG(PRI_RING_TAIL, 0); 1649f7018c21STomi Valkeinen OUTREG(PRI_RING_HEAD, 0); 1650f7018c21STomi Valkeinen 1651f7018c21STomi Valkeinen OUTREG(PRI_RING_START, dinfo->ring.physical & RING_START_MASK); 1652f7018c21STomi Valkeinen OUTREG(PRI_RING_LENGTH, 1653f7018c21STomi Valkeinen ((dinfo->ring.size - GTT_PAGE_SIZE) & RING_LENGTH_MASK) | 1654f7018c21STomi Valkeinen RING_NO_REPORT | RING_ENABLE); 1655f7018c21STomi Valkeinen refresh_ring(dinfo); 1656f7018c21STomi Valkeinen dinfo->ring_active = 1; 1657f7018c21STomi Valkeinen } 1658f7018c21STomi Valkeinen 1659f7018c21STomi Valkeinen /* 2D fillrect (solid fill or invert) */ 1660f7018c21STomi Valkeinen void intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, 1661f7018c21STomi Valkeinen u32 h, u32 color, u32 pitch, u32 bpp, u32 rop) 1662f7018c21STomi Valkeinen { 1663f7018c21STomi Valkeinen u32 br00, br09, br13, br14, br16; 1664f7018c21STomi Valkeinen 1665f7018c21STomi Valkeinen #if VERBOSE > 0 1666f7018c21STomi Valkeinen DBG_MSG("intelfbhw_do_fillrect: (%d,%d) %dx%d, c 0x%06x, p %d bpp %d, " 1667f7018c21STomi Valkeinen "rop 0x%02x\n", x, y, w, h, color, pitch, bpp, rop); 1668f7018c21STomi Valkeinen #endif 1669f7018c21STomi Valkeinen 1670f7018c21STomi Valkeinen br00 = COLOR_BLT_CMD; 1671f7018c21STomi Valkeinen br09 = dinfo->fb_start + (y * pitch + x * (bpp / 8)); 1672f7018c21STomi Valkeinen br13 = (rop << ROP_SHIFT) | pitch; 1673f7018c21STomi Valkeinen br14 = (h << HEIGHT_SHIFT) | ((w * (bpp / 8)) << WIDTH_SHIFT); 1674f7018c21STomi Valkeinen br16 = color; 1675f7018c21STomi Valkeinen 1676f7018c21STomi Valkeinen switch (bpp) { 1677f7018c21STomi Valkeinen case 8: 1678f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_8; 1679f7018c21STomi Valkeinen break; 1680f7018c21STomi Valkeinen case 16: 1681f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_16; 1682f7018c21STomi Valkeinen break; 1683f7018c21STomi Valkeinen case 32: 1684f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_32; 1685f7018c21STomi Valkeinen br00 |= WRITE_ALPHA | WRITE_RGB; 1686f7018c21STomi Valkeinen break; 1687f7018c21STomi Valkeinen } 1688f7018c21STomi Valkeinen 1689f7018c21STomi Valkeinen START_RING(6); 1690f7018c21STomi Valkeinen OUT_RING(br00); 1691f7018c21STomi Valkeinen OUT_RING(br13); 1692f7018c21STomi Valkeinen OUT_RING(br14); 1693f7018c21STomi Valkeinen OUT_RING(br09); 1694f7018c21STomi Valkeinen OUT_RING(br16); 1695f7018c21STomi Valkeinen OUT_RING(MI_NOOP); 1696f7018c21STomi Valkeinen ADVANCE_RING(); 1697f7018c21STomi Valkeinen 1698f7018c21STomi Valkeinen #if VERBOSE > 0 1699f7018c21STomi Valkeinen DBG_MSG("ring = 0x%08x, 0x%08x (%d)\n", dinfo->ring_head, 1700f7018c21STomi Valkeinen dinfo->ring_tail, dinfo->ring_space); 1701f7018c21STomi Valkeinen #endif 1702f7018c21STomi Valkeinen } 1703f7018c21STomi Valkeinen 1704f7018c21STomi Valkeinen void 1705f7018c21STomi Valkeinen intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury, 1706f7018c21STomi Valkeinen u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch, u32 bpp) 1707f7018c21STomi Valkeinen { 1708f7018c21STomi Valkeinen u32 br00, br09, br11, br12, br13, br22, br23, br26; 1709f7018c21STomi Valkeinen 1710f7018c21STomi Valkeinen #if VERBOSE > 0 1711f7018c21STomi Valkeinen DBG_MSG("intelfbhw_do_bitblt: (%d,%d)->(%d,%d) %dx%d, p %d bpp %d\n", 1712f7018c21STomi Valkeinen curx, cury, dstx, dsty, w, h, pitch, bpp); 1713f7018c21STomi Valkeinen #endif 1714f7018c21STomi Valkeinen 1715f7018c21STomi Valkeinen br00 = XY_SRC_COPY_BLT_CMD; 1716f7018c21STomi Valkeinen br09 = dinfo->fb_start; 1717f7018c21STomi Valkeinen br11 = (pitch << PITCH_SHIFT); 1718f7018c21STomi Valkeinen br12 = dinfo->fb_start; 1719f7018c21STomi Valkeinen br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT); 1720f7018c21STomi Valkeinen br22 = (dstx << WIDTH_SHIFT) | (dsty << HEIGHT_SHIFT); 1721f7018c21STomi Valkeinen br23 = ((dstx + w) << WIDTH_SHIFT) | 1722f7018c21STomi Valkeinen ((dsty + h) << HEIGHT_SHIFT); 1723f7018c21STomi Valkeinen br26 = (curx << WIDTH_SHIFT) | (cury << HEIGHT_SHIFT); 1724f7018c21STomi Valkeinen 1725f7018c21STomi Valkeinen switch (bpp) { 1726f7018c21STomi Valkeinen case 8: 1727f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_8; 1728f7018c21STomi Valkeinen break; 1729f7018c21STomi Valkeinen case 16: 1730f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_16; 1731f7018c21STomi Valkeinen break; 1732f7018c21STomi Valkeinen case 32: 1733f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_32; 1734f7018c21STomi Valkeinen br00 |= WRITE_ALPHA | WRITE_RGB; 1735f7018c21STomi Valkeinen break; 1736f7018c21STomi Valkeinen } 1737f7018c21STomi Valkeinen 1738f7018c21STomi Valkeinen START_RING(8); 1739f7018c21STomi Valkeinen OUT_RING(br00); 1740f7018c21STomi Valkeinen OUT_RING(br13); 1741f7018c21STomi Valkeinen OUT_RING(br22); 1742f7018c21STomi Valkeinen OUT_RING(br23); 1743f7018c21STomi Valkeinen OUT_RING(br09); 1744f7018c21STomi Valkeinen OUT_RING(br26); 1745f7018c21STomi Valkeinen OUT_RING(br11); 1746f7018c21STomi Valkeinen OUT_RING(br12); 1747f7018c21STomi Valkeinen ADVANCE_RING(); 1748f7018c21STomi Valkeinen } 1749f7018c21STomi Valkeinen 1750f7018c21STomi Valkeinen int intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, 1751f7018c21STomi Valkeinen u32 h, const u8* cdat, u32 x, u32 y, u32 pitch, 1752f7018c21STomi Valkeinen u32 bpp) 1753f7018c21STomi Valkeinen { 1754f7018c21STomi Valkeinen int nbytes, ndwords, pad, tmp; 1755f7018c21STomi Valkeinen u32 br00, br09, br13, br18, br19, br22, br23; 1756f7018c21STomi Valkeinen int dat, ix, iy, iw; 1757f7018c21STomi Valkeinen int i, j; 1758f7018c21STomi Valkeinen 1759f7018c21STomi Valkeinen #if VERBOSE > 0 1760f7018c21STomi Valkeinen DBG_MSG("intelfbhw_do_drawglyph: (%d,%d) %dx%d\n", x, y, w, h); 1761f7018c21STomi Valkeinen #endif 1762f7018c21STomi Valkeinen 1763f7018c21STomi Valkeinen /* size in bytes of a padded scanline */ 1764f7018c21STomi Valkeinen nbytes = ROUND_UP_TO(w, 16) / 8; 1765f7018c21STomi Valkeinen 1766f7018c21STomi Valkeinen /* Total bytes of padded scanline data to write out. */ 1767f7018c21STomi Valkeinen nbytes = nbytes * h; 1768f7018c21STomi Valkeinen 1769f7018c21STomi Valkeinen /* 1770f7018c21STomi Valkeinen * Check if the glyph data exceeds the immediate mode limit. 1771f7018c21STomi Valkeinen * It would take a large font (1K pixels) to hit this limit. 1772f7018c21STomi Valkeinen */ 1773f7018c21STomi Valkeinen if (nbytes > MAX_MONO_IMM_SIZE) 1774f7018c21STomi Valkeinen return 0; 1775f7018c21STomi Valkeinen 1776f7018c21STomi Valkeinen /* Src data is packaged a dword (32-bit) at a time. */ 1777f7018c21STomi Valkeinen ndwords = ROUND_UP_TO(nbytes, 4) / 4; 1778f7018c21STomi Valkeinen 1779f7018c21STomi Valkeinen /* 1780f7018c21STomi Valkeinen * Ring has to be padded to a quad word. But because the command starts 1781f7018c21STomi Valkeinen with 7 bytes, pad only if there is an even number of ndwords 1782f7018c21STomi Valkeinen */ 1783f7018c21STomi Valkeinen pad = !(ndwords % 2); 1784f7018c21STomi Valkeinen 1785f7018c21STomi Valkeinen tmp = (XY_MONO_SRC_IMM_BLT_CMD & DW_LENGTH_MASK) + ndwords; 1786f7018c21STomi Valkeinen br00 = (XY_MONO_SRC_IMM_BLT_CMD & ~DW_LENGTH_MASK) | tmp; 1787f7018c21STomi Valkeinen br09 = dinfo->fb_start; 1788f7018c21STomi Valkeinen br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT); 1789f7018c21STomi Valkeinen br18 = bg; 1790f7018c21STomi Valkeinen br19 = fg; 1791f7018c21STomi Valkeinen br22 = (x << WIDTH_SHIFT) | (y << HEIGHT_SHIFT); 1792f7018c21STomi Valkeinen br23 = ((x + w) << WIDTH_SHIFT) | ((y + h) << HEIGHT_SHIFT); 1793f7018c21STomi Valkeinen 1794f7018c21STomi Valkeinen switch (bpp) { 1795f7018c21STomi Valkeinen case 8: 1796f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_8; 1797f7018c21STomi Valkeinen break; 1798f7018c21STomi Valkeinen case 16: 1799f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_16; 1800f7018c21STomi Valkeinen break; 1801f7018c21STomi Valkeinen case 32: 1802f7018c21STomi Valkeinen br13 |= COLOR_DEPTH_32; 1803f7018c21STomi Valkeinen br00 |= WRITE_ALPHA | WRITE_RGB; 1804f7018c21STomi Valkeinen break; 1805f7018c21STomi Valkeinen } 1806f7018c21STomi Valkeinen 1807f7018c21STomi Valkeinen START_RING(8 + ndwords); 1808f7018c21STomi Valkeinen OUT_RING(br00); 1809f7018c21STomi Valkeinen OUT_RING(br13); 1810f7018c21STomi Valkeinen OUT_RING(br22); 1811f7018c21STomi Valkeinen OUT_RING(br23); 1812f7018c21STomi Valkeinen OUT_RING(br09); 1813f7018c21STomi Valkeinen OUT_RING(br18); 1814f7018c21STomi Valkeinen OUT_RING(br19); 1815f7018c21STomi Valkeinen ix = iy = 0; 1816f7018c21STomi Valkeinen iw = ROUND_UP_TO(w, 8) / 8; 1817f7018c21STomi Valkeinen while (ndwords--) { 1818f7018c21STomi Valkeinen dat = 0; 1819f7018c21STomi Valkeinen for (j = 0; j < 2; ++j) { 1820f7018c21STomi Valkeinen for (i = 0; i < 2; ++i) { 1821f7018c21STomi Valkeinen if (ix != iw || i == 0) 1822f7018c21STomi Valkeinen dat |= cdat[iy*iw + ix++] << (i+j*2)*8; 1823f7018c21STomi Valkeinen } 1824f7018c21STomi Valkeinen if (ix == iw && iy != (h-1)) { 1825f7018c21STomi Valkeinen ix = 0; 1826f7018c21STomi Valkeinen ++iy; 1827f7018c21STomi Valkeinen } 1828f7018c21STomi Valkeinen } 1829f7018c21STomi Valkeinen OUT_RING(dat); 1830f7018c21STomi Valkeinen } 1831f7018c21STomi Valkeinen if (pad) 1832f7018c21STomi Valkeinen OUT_RING(MI_NOOP); 1833f7018c21STomi Valkeinen ADVANCE_RING(); 1834f7018c21STomi Valkeinen 1835f7018c21STomi Valkeinen return 1; 1836f7018c21STomi Valkeinen } 1837f7018c21STomi Valkeinen 1838f7018c21STomi Valkeinen /* HW cursor functions. */ 1839f7018c21STomi Valkeinen void intelfbhw_cursor_init(struct intelfb_info *dinfo) 1840f7018c21STomi Valkeinen { 1841f7018c21STomi Valkeinen u32 tmp; 1842f7018c21STomi Valkeinen 1843f7018c21STomi Valkeinen #if VERBOSE > 0 1844f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_init\n"); 1845f7018c21STomi Valkeinen #endif 1846f7018c21STomi Valkeinen 1847f7018c21STomi Valkeinen if (dinfo->mobile || IS_I9XX(dinfo)) { 1848f7018c21STomi Valkeinen if (!dinfo->cursor.physical) 1849f7018c21STomi Valkeinen return; 1850f7018c21STomi Valkeinen tmp = INREG(CURSOR_A_CONTROL); 1851f7018c21STomi Valkeinen tmp &= ~(CURSOR_MODE_MASK | CURSOR_MOBILE_GAMMA_ENABLE | 1852f7018c21STomi Valkeinen CURSOR_MEM_TYPE_LOCAL | 1853f7018c21STomi Valkeinen (1 << CURSOR_PIPE_SELECT_SHIFT)); 1854f7018c21STomi Valkeinen tmp |= CURSOR_MODE_DISABLE; 1855f7018c21STomi Valkeinen OUTREG(CURSOR_A_CONTROL, tmp); 1856f7018c21STomi Valkeinen OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); 1857f7018c21STomi Valkeinen } else { 1858f7018c21STomi Valkeinen tmp = INREG(CURSOR_CONTROL); 1859f7018c21STomi Valkeinen tmp &= ~(CURSOR_FORMAT_MASK | CURSOR_GAMMA_ENABLE | 1860f7018c21STomi Valkeinen CURSOR_ENABLE | CURSOR_STRIDE_MASK); 18612079a513SJulia Lawall tmp |= CURSOR_FORMAT_3C; 1862f7018c21STomi Valkeinen OUTREG(CURSOR_CONTROL, tmp); 1863f7018c21STomi Valkeinen OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.offset << 12); 1864f7018c21STomi Valkeinen tmp = (64 << CURSOR_SIZE_H_SHIFT) | 1865f7018c21STomi Valkeinen (64 << CURSOR_SIZE_V_SHIFT); 1866f7018c21STomi Valkeinen OUTREG(CURSOR_SIZE, tmp); 1867f7018c21STomi Valkeinen } 1868f7018c21STomi Valkeinen } 1869f7018c21STomi Valkeinen 1870f7018c21STomi Valkeinen void intelfbhw_cursor_hide(struct intelfb_info *dinfo) 1871f7018c21STomi Valkeinen { 1872f7018c21STomi Valkeinen u32 tmp; 1873f7018c21STomi Valkeinen 1874f7018c21STomi Valkeinen #if VERBOSE > 0 1875f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_hide\n"); 1876f7018c21STomi Valkeinen #endif 1877f7018c21STomi Valkeinen 1878f7018c21STomi Valkeinen dinfo->cursor_on = 0; 1879f7018c21STomi Valkeinen if (dinfo->mobile || IS_I9XX(dinfo)) { 1880f7018c21STomi Valkeinen if (!dinfo->cursor.physical) 1881f7018c21STomi Valkeinen return; 1882f7018c21STomi Valkeinen tmp = INREG(CURSOR_A_CONTROL); 1883f7018c21STomi Valkeinen tmp &= ~CURSOR_MODE_MASK; 1884f7018c21STomi Valkeinen tmp |= CURSOR_MODE_DISABLE; 1885f7018c21STomi Valkeinen OUTREG(CURSOR_A_CONTROL, tmp); 1886f7018c21STomi Valkeinen /* Flush changes */ 1887f7018c21STomi Valkeinen OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); 1888f7018c21STomi Valkeinen } else { 1889f7018c21STomi Valkeinen tmp = INREG(CURSOR_CONTROL); 1890f7018c21STomi Valkeinen tmp &= ~CURSOR_ENABLE; 1891f7018c21STomi Valkeinen OUTREG(CURSOR_CONTROL, tmp); 1892f7018c21STomi Valkeinen } 1893f7018c21STomi Valkeinen } 1894f7018c21STomi Valkeinen 1895f7018c21STomi Valkeinen void intelfbhw_cursor_show(struct intelfb_info *dinfo) 1896f7018c21STomi Valkeinen { 1897f7018c21STomi Valkeinen u32 tmp; 1898f7018c21STomi Valkeinen 1899f7018c21STomi Valkeinen #if VERBOSE > 0 1900f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_show\n"); 1901f7018c21STomi Valkeinen #endif 1902f7018c21STomi Valkeinen 1903f7018c21STomi Valkeinen dinfo->cursor_on = 1; 1904f7018c21STomi Valkeinen 1905f7018c21STomi Valkeinen if (dinfo->cursor_blanked) 1906f7018c21STomi Valkeinen return; 1907f7018c21STomi Valkeinen 1908f7018c21STomi Valkeinen if (dinfo->mobile || IS_I9XX(dinfo)) { 1909f7018c21STomi Valkeinen if (!dinfo->cursor.physical) 1910f7018c21STomi Valkeinen return; 1911f7018c21STomi Valkeinen tmp = INREG(CURSOR_A_CONTROL); 1912f7018c21STomi Valkeinen tmp &= ~CURSOR_MODE_MASK; 1913f7018c21STomi Valkeinen tmp |= CURSOR_MODE_64_4C_AX; 1914f7018c21STomi Valkeinen OUTREG(CURSOR_A_CONTROL, tmp); 1915f7018c21STomi Valkeinen /* Flush changes */ 1916f7018c21STomi Valkeinen OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); 1917f7018c21STomi Valkeinen } else { 1918f7018c21STomi Valkeinen tmp = INREG(CURSOR_CONTROL); 1919f7018c21STomi Valkeinen tmp |= CURSOR_ENABLE; 1920f7018c21STomi Valkeinen OUTREG(CURSOR_CONTROL, tmp); 1921f7018c21STomi Valkeinen } 1922f7018c21STomi Valkeinen } 1923f7018c21STomi Valkeinen 1924f7018c21STomi Valkeinen void intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) 1925f7018c21STomi Valkeinen { 1926f7018c21STomi Valkeinen u32 tmp; 1927f7018c21STomi Valkeinen 1928f7018c21STomi Valkeinen #if VERBOSE > 0 1929f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_setpos: (%d, %d)\n", x, y); 1930f7018c21STomi Valkeinen #endif 1931f7018c21STomi Valkeinen 1932f7018c21STomi Valkeinen /* 1933f7018c21STomi Valkeinen * Sets the position. The coordinates are assumed to already 1934f7018c21STomi Valkeinen * have any offset adjusted. Assume that the cursor is never 1935f7018c21STomi Valkeinen * completely off-screen, and that x, y are always >= 0. 1936f7018c21STomi Valkeinen */ 1937f7018c21STomi Valkeinen 1938f7018c21STomi Valkeinen tmp = ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) | 1939f7018c21STomi Valkeinen ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); 1940f7018c21STomi Valkeinen OUTREG(CURSOR_A_POSITION, tmp); 1941f7018c21STomi Valkeinen 1942f7018c21STomi Valkeinen if (IS_I9XX(dinfo)) 1943f7018c21STomi Valkeinen OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); 1944f7018c21STomi Valkeinen } 1945f7018c21STomi Valkeinen 1946f7018c21STomi Valkeinen void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg) 1947f7018c21STomi Valkeinen { 1948f7018c21STomi Valkeinen #if VERBOSE > 0 1949f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_setcolor\n"); 1950f7018c21STomi Valkeinen #endif 1951f7018c21STomi Valkeinen 1952f7018c21STomi Valkeinen OUTREG(CURSOR_A_PALETTE0, bg & CURSOR_PALETTE_MASK); 1953f7018c21STomi Valkeinen OUTREG(CURSOR_A_PALETTE1, fg & CURSOR_PALETTE_MASK); 1954f7018c21STomi Valkeinen OUTREG(CURSOR_A_PALETTE2, fg & CURSOR_PALETTE_MASK); 1955f7018c21STomi Valkeinen OUTREG(CURSOR_A_PALETTE3, bg & CURSOR_PALETTE_MASK); 1956f7018c21STomi Valkeinen } 1957f7018c21STomi Valkeinen 1958f7018c21STomi Valkeinen void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, 1959f7018c21STomi Valkeinen u8 *data) 1960f7018c21STomi Valkeinen { 1961f7018c21STomi Valkeinen u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; 1962f7018c21STomi Valkeinen int i, j, w = width / 8; 1963f7018c21STomi Valkeinen int mod = width % 8, t_mask, d_mask; 1964f7018c21STomi Valkeinen 1965f7018c21STomi Valkeinen #if VERBOSE > 0 1966f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_load\n"); 1967f7018c21STomi Valkeinen #endif 1968f7018c21STomi Valkeinen 1969f7018c21STomi Valkeinen if (!dinfo->cursor.virtual) 1970f7018c21STomi Valkeinen return; 1971f7018c21STomi Valkeinen 1972f7018c21STomi Valkeinen t_mask = 0xff >> mod; 1973f7018c21STomi Valkeinen d_mask = ~(0xff >> mod); 1974f7018c21STomi Valkeinen for (i = height; i--; ) { 1975f7018c21STomi Valkeinen for (j = 0; j < w; j++) { 1976f7018c21STomi Valkeinen writeb(0x00, addr + j); 1977f7018c21STomi Valkeinen writeb(*(data++), addr + j+8); 1978f7018c21STomi Valkeinen } 1979f7018c21STomi Valkeinen if (mod) { 1980f7018c21STomi Valkeinen writeb(t_mask, addr + j); 1981f7018c21STomi Valkeinen writeb(*(data++) & d_mask, addr + j+8); 1982f7018c21STomi Valkeinen } 1983f7018c21STomi Valkeinen addr += 16; 1984f7018c21STomi Valkeinen } 1985f7018c21STomi Valkeinen } 1986f7018c21STomi Valkeinen 1987f7018c21STomi Valkeinen void intelfbhw_cursor_reset(struct intelfb_info *dinfo) 1988f7018c21STomi Valkeinen { 1989f7018c21STomi Valkeinen u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; 1990f7018c21STomi Valkeinen int i, j; 1991f7018c21STomi Valkeinen 1992f7018c21STomi Valkeinen #if VERBOSE > 0 1993f7018c21STomi Valkeinen DBG_MSG("intelfbhw_cursor_reset\n"); 1994f7018c21STomi Valkeinen #endif 1995f7018c21STomi Valkeinen 1996f7018c21STomi Valkeinen if (!dinfo->cursor.virtual) 1997f7018c21STomi Valkeinen return; 1998f7018c21STomi Valkeinen 1999f7018c21STomi Valkeinen for (i = 64; i--; ) { 2000f7018c21STomi Valkeinen for (j = 0; j < 8; j++) { 2001f7018c21STomi Valkeinen writeb(0xff, addr + j+0); 2002f7018c21STomi Valkeinen writeb(0x00, addr + j+8); 2003f7018c21STomi Valkeinen } 2004f7018c21STomi Valkeinen addr += 16; 2005f7018c21STomi Valkeinen } 2006f7018c21STomi Valkeinen } 2007f7018c21STomi Valkeinen 2008f7018c21STomi Valkeinen static irqreturn_t intelfbhw_irq(int irq, void *dev_id) 2009f7018c21STomi Valkeinen { 2010f7018c21STomi Valkeinen u16 tmp; 2011f7018c21STomi Valkeinen struct intelfb_info *dinfo = dev_id; 2012f7018c21STomi Valkeinen 2013f7018c21STomi Valkeinen spin_lock(&dinfo->int_lock); 2014f7018c21STomi Valkeinen 2015f7018c21STomi Valkeinen tmp = INREG16(IIR); 2016f7018c21STomi Valkeinen if (dinfo->info->var.vmode & FB_VMODE_INTERLACED) 2017f7018c21STomi Valkeinen tmp &= PIPE_A_EVENT_INTERRUPT; 2018f7018c21STomi Valkeinen else 2019f7018c21STomi Valkeinen tmp &= VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */ 2020f7018c21STomi Valkeinen 2021f7018c21STomi Valkeinen if (tmp == 0) { 2022f7018c21STomi Valkeinen spin_unlock(&dinfo->int_lock); 2023f7018c21STomi Valkeinen return IRQ_RETVAL(0); /* not us */ 2024f7018c21STomi Valkeinen } 2025f7018c21STomi Valkeinen 2026f7018c21STomi Valkeinen /* clear status bits 0-15 ASAP and don't touch bits 16-31 */ 2027f7018c21STomi Valkeinen OUTREG(PIPEASTAT, INREG(PIPEASTAT)); 2028f7018c21STomi Valkeinen 2029f7018c21STomi Valkeinen OUTREG16(IIR, tmp); 2030f7018c21STomi Valkeinen if (dinfo->vsync.pan_display) { 2031f7018c21STomi Valkeinen dinfo->vsync.pan_display = 0; 2032f7018c21STomi Valkeinen OUTREG(DSPABASE, dinfo->vsync.pan_offset); 2033f7018c21STomi Valkeinen } 2034f7018c21STomi Valkeinen 2035f7018c21STomi Valkeinen dinfo->vsync.count++; 2036f7018c21STomi Valkeinen wake_up_interruptible(&dinfo->vsync.wait); 2037f7018c21STomi Valkeinen 2038f7018c21STomi Valkeinen spin_unlock(&dinfo->int_lock); 2039f7018c21STomi Valkeinen 2040f7018c21STomi Valkeinen return IRQ_RETVAL(1); 2041f7018c21STomi Valkeinen } 2042f7018c21STomi Valkeinen 2043f7018c21STomi Valkeinen int intelfbhw_enable_irq(struct intelfb_info *dinfo) 2044f7018c21STomi Valkeinen { 2045f7018c21STomi Valkeinen u16 tmp; 2046f7018c21STomi Valkeinen if (!test_and_set_bit(0, &dinfo->irq_flags)) { 2047f7018c21STomi Valkeinen if (request_irq(dinfo->pdev->irq, intelfbhw_irq, IRQF_SHARED, 2048f7018c21STomi Valkeinen "intelfb", dinfo)) { 2049f7018c21STomi Valkeinen clear_bit(0, &dinfo->irq_flags); 2050f7018c21STomi Valkeinen return -EINVAL; 2051f7018c21STomi Valkeinen } 2052f7018c21STomi Valkeinen 2053f7018c21STomi Valkeinen spin_lock_irq(&dinfo->int_lock); 2054f7018c21STomi Valkeinen OUTREG16(HWSTAM, 0xfffe); /* i830 DRM uses ffff */ 2055f7018c21STomi Valkeinen OUTREG16(IMR, 0); 2056f7018c21STomi Valkeinen } else 2057f7018c21STomi Valkeinen spin_lock_irq(&dinfo->int_lock); 2058f7018c21STomi Valkeinen 2059f7018c21STomi Valkeinen if (dinfo->info->var.vmode & FB_VMODE_INTERLACED) 2060f7018c21STomi Valkeinen tmp = PIPE_A_EVENT_INTERRUPT; 2061f7018c21STomi Valkeinen else 2062f7018c21STomi Valkeinen tmp = VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */ 2063f7018c21STomi Valkeinen if (tmp != INREG16(IER)) { 2064f7018c21STomi Valkeinen DBG_MSG("changing IER to 0x%X\n", tmp); 2065f7018c21STomi Valkeinen OUTREG16(IER, tmp); 2066f7018c21STomi Valkeinen } 2067f7018c21STomi Valkeinen 2068f7018c21STomi Valkeinen spin_unlock_irq(&dinfo->int_lock); 2069f7018c21STomi Valkeinen return 0; 2070f7018c21STomi Valkeinen } 2071f7018c21STomi Valkeinen 2072f7018c21STomi Valkeinen void intelfbhw_disable_irq(struct intelfb_info *dinfo) 2073f7018c21STomi Valkeinen { 2074f7018c21STomi Valkeinen if (test_and_clear_bit(0, &dinfo->irq_flags)) { 2075f7018c21STomi Valkeinen if (dinfo->vsync.pan_display) { 2076f7018c21STomi Valkeinen dinfo->vsync.pan_display = 0; 2077f7018c21STomi Valkeinen OUTREG(DSPABASE, dinfo->vsync.pan_offset); 2078f7018c21STomi Valkeinen } 2079f7018c21STomi Valkeinen spin_lock_irq(&dinfo->int_lock); 2080f7018c21STomi Valkeinen OUTREG16(HWSTAM, 0xffff); 2081f7018c21STomi Valkeinen OUTREG16(IMR, 0xffff); 2082f7018c21STomi Valkeinen OUTREG16(IER, 0x0); 2083f7018c21STomi Valkeinen 2084f7018c21STomi Valkeinen OUTREG16(IIR, INREG16(IIR)); /* clear IRQ requests */ 2085f7018c21STomi Valkeinen spin_unlock_irq(&dinfo->int_lock); 2086f7018c21STomi Valkeinen 2087f7018c21STomi Valkeinen free_irq(dinfo->pdev->irq, dinfo); 2088f7018c21STomi Valkeinen } 2089f7018c21STomi Valkeinen } 2090f7018c21STomi Valkeinen 2091f7018c21STomi Valkeinen int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) 2092f7018c21STomi Valkeinen { 2093f7018c21STomi Valkeinen struct intelfb_vsync *vsync; 2094f7018c21STomi Valkeinen unsigned int count; 2095f7018c21STomi Valkeinen int ret; 2096f7018c21STomi Valkeinen 2097f7018c21STomi Valkeinen switch (pipe) { 2098f7018c21STomi Valkeinen case 0: 2099f7018c21STomi Valkeinen vsync = &dinfo->vsync; 2100f7018c21STomi Valkeinen break; 2101f7018c21STomi Valkeinen default: 2102f7018c21STomi Valkeinen return -ENODEV; 2103f7018c21STomi Valkeinen } 2104f7018c21STomi Valkeinen 2105f7018c21STomi Valkeinen ret = intelfbhw_enable_irq(dinfo); 2106f7018c21STomi Valkeinen if (ret) 2107f7018c21STomi Valkeinen return ret; 2108f7018c21STomi Valkeinen 2109f7018c21STomi Valkeinen count = vsync->count; 2110f7018c21STomi Valkeinen ret = wait_event_interruptible_timeout(vsync->wait, 2111f7018c21STomi Valkeinen count != vsync->count, HZ / 10); 2112f7018c21STomi Valkeinen if (ret < 0) 2113f7018c21STomi Valkeinen return ret; 2114f7018c21STomi Valkeinen if (ret == 0) { 2115f7018c21STomi Valkeinen DBG_MSG("wait_for_vsync timed out!\n"); 2116f7018c21STomi Valkeinen return -ETIMEDOUT; 2117f7018c21STomi Valkeinen } 2118f7018c21STomi Valkeinen 2119f7018c21STomi Valkeinen return 0; 2120f7018c21STomi Valkeinen } 2121