1fa225cbcSrjs/* 2fa225cbcSrjs * Copyright © 2006 Intel Corporation 3fa225cbcSrjs * 4fa225cbcSrjs * Permission is hereby granted, free of charge, to any person obtaining a 5fa225cbcSrjs * copy of this software and associated documentation files (the "Software"), 6fa225cbcSrjs * to deal in the Software without restriction, including without limitation 7fa225cbcSrjs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8fa225cbcSrjs * and/or sell copies of the Software, and to permit persons to whom the 9fa225cbcSrjs * Software is furnished to do so, subject to the following conditions: 10fa225cbcSrjs * 11fa225cbcSrjs * The above copyright notice and this permission notice (including the next 12fa225cbcSrjs * paragraph) shall be included in all copies or substantial portions of the 13fa225cbcSrjs * Software. 14fa225cbcSrjs * 15fa225cbcSrjs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16fa225cbcSrjs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17fa225cbcSrjs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18fa225cbcSrjs * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19fa225cbcSrjs * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20fa225cbcSrjs * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21fa225cbcSrjs * DEALINGS IN THE SOFTWARE. 22fa225cbcSrjs * 23fa225cbcSrjs * Authors: 24fa225cbcSrjs * Eric Anholt <eric@anholt.net> 25fa225cbcSrjs * 26fa225cbcSrjs */ 27fa225cbcSrjs 28fa225cbcSrjs#ifdef HAVE_CONFIG_H 29fa225cbcSrjs#include "config.h" 30fa225cbcSrjs#endif 31fa225cbcSrjs 32fa225cbcSrjs#include "xf86.h" 33fa225cbcSrjs#include "xf86_OSproc.h" 34fa225cbcSrjs#include "compiler.h" 35fa225cbcSrjs#include "miscstruct.h" 36fa225cbcSrjs#include "xf86i2c.h" 37fa225cbcSrjs#include "xf86Crtc.h" 38fa225cbcSrjs#ifdef HAVE_XEXTPROTO_71 39fa225cbcSrjs#include <X11/extensions/dpmsconst.h> 40fa225cbcSrjs#else 41fa225cbcSrjs#define DPMS_SERVER 42fa225cbcSrjs#include <X11/extensions/dpms.h> 43fa225cbcSrjs#endif 44fa225cbcSrjs 45fa225cbcSrjs#include <unistd.h> 46fa225cbcSrjs 47fa225cbcSrjs#include "../i2c_vid.h" 48fa225cbcSrjs#include "../i830_bios.h" 49fa225cbcSrjs#include "ivch_reg.h" 50fa225cbcSrjs 51fa225cbcSrjsstruct ivch_priv { 52fa225cbcSrjs I2CDevRec d; 53fa225cbcSrjs 54fa225cbcSrjs xf86OutputPtr output; 55fa225cbcSrjs Bool quiet; 56fa225cbcSrjs 57fa225cbcSrjs uint16_t width, height; 58fa225cbcSrjs 59fa225cbcSrjs uint16_t save_VR01; 60fa225cbcSrjs uint16_t save_VR40; 61fa225cbcSrjs}; 62fa225cbcSrjs 63fa225cbcSrjsstruct vch_capabilities { 64fa225cbcSrjs struct aimdb_block aimdb_block; 65fa225cbcSrjs uint8_t panel_type; 66fa225cbcSrjs uint8_t set_panel_type; 67fa225cbcSrjs uint8_t slave_address; 68fa225cbcSrjs uint8_t capabilities; 69fa225cbcSrjs#define VCH_PANEL_FITTING_SUPPORT (0x3 << 0) 70fa225cbcSrjs#define VCH_PANEL_FITTING_TEXT (1 << 2) 71fa225cbcSrjs#define VCH_PANEL_FITTING_GRAPHICS (1 << 3) 72fa225cbcSrjs#define VCH_PANEL_FITTING_RATIO (1 << 4) 73fa225cbcSrjs#define VCH_DITHERING (1 << 5) 74fa225cbcSrjs uint8_t backlight_gpio; 75fa225cbcSrjs uint8_t set_panel_type_us_gpios; 76fa225cbcSrjs} __attribute__ ((packed)); 77fa225cbcSrjs 78fa225cbcSrjsstatic void 79fa225cbcSrjsivch_dump_regs(I2CDevPtr d); 80fa225cbcSrjs 81fa225cbcSrjs/** 82fa225cbcSrjs * Reads a register on the ivch. 83fa225cbcSrjs * 84fa225cbcSrjs * Each of the 256 registers are 16 bits long. 85fa225cbcSrjs */ 86fa225cbcSrjsstatic Bool 87fa225cbcSrjsivch_read(struct ivch_priv *priv, int addr, uint16_t *data) 88fa225cbcSrjs{ 89fa225cbcSrjs I2CBusPtr b = priv->d.pI2CBus; 90fa225cbcSrjs I2CByte *p = (I2CByte *) data; 91fa225cbcSrjs 92fa225cbcSrjs if (!b->I2CStart(b, priv->d.StartTimeout)) 93fa225cbcSrjs goto fail; 94fa225cbcSrjs 95fa225cbcSrjs if (!b->I2CPutByte(&priv->d, priv->d.SlaveAddr | 1)) 96fa225cbcSrjs goto fail; 97fa225cbcSrjs 98fa225cbcSrjs if (!b->I2CPutByte(&priv->d, addr)) 99fa225cbcSrjs goto fail; 100fa225cbcSrjs 101fa225cbcSrjs if (!b->I2CGetByte(&priv->d, p++, FALSE)) 102fa225cbcSrjs goto fail; 103fa225cbcSrjs 104fa225cbcSrjs if (!b->I2CGetByte(&priv->d, p++, TRUE)) 105fa225cbcSrjs goto fail; 106fa225cbcSrjs 107fa225cbcSrjs b->I2CStop(&priv->d); 108fa225cbcSrjs 109fa225cbcSrjs return TRUE; 110fa225cbcSrjs 111fa225cbcSrjs fail: 112fa225cbcSrjs if (!priv->quiet) { 113fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 114fa225cbcSrjs "ivch: Unable to read register 0x%02x from %s:%02x.\n", 115fa225cbcSrjs addr, priv->d.pI2CBus->BusName, priv->d.SlaveAddr); 116fa225cbcSrjs } 117fa225cbcSrjs b->I2CStop(&priv->d); 118fa225cbcSrjs 119fa225cbcSrjs return FALSE; 120fa225cbcSrjs} 121fa225cbcSrjs 122fa225cbcSrjs/** Writes a 16-bit register on the ivch */ 123fa225cbcSrjsstatic Bool 124fa225cbcSrjsivch_write(struct ivch_priv *priv, int addr, uint16_t data) 125fa225cbcSrjs{ 126fa225cbcSrjs I2CBusPtr b = priv->d.pI2CBus; 127fa225cbcSrjs 128fa225cbcSrjs if (!b->I2CStart(b, priv->d.StartTimeout)) 129fa225cbcSrjs goto fail; 130fa225cbcSrjs 131fa225cbcSrjs if (!b->I2CPutByte(&priv->d, priv->d.SlaveAddr)) 132fa225cbcSrjs goto fail; 133fa225cbcSrjs 134fa225cbcSrjs if (!b->I2CPutByte(&priv->d, addr)) 135fa225cbcSrjs goto fail; 136fa225cbcSrjs 137fa225cbcSrjs if (!b->I2CPutByte(&priv->d, data & 0xff)) 138fa225cbcSrjs goto fail; 139fa225cbcSrjs 140fa225cbcSrjs if (!b->I2CPutByte(&priv->d, data >> 8)) 141fa225cbcSrjs goto fail; 142fa225cbcSrjs 143fa225cbcSrjs b->I2CStop(&priv->d); 144fa225cbcSrjs 145fa225cbcSrjs return TRUE; 146fa225cbcSrjs 147fa225cbcSrjs fail: 148fa225cbcSrjs b->I2CStop(&priv->d); 149fa225cbcSrjs 150fa225cbcSrjs if (!priv->quiet) { 151fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 152fa225cbcSrjs "Unable to write register 0x%02x to %s:%d.\n", 153fa225cbcSrjs addr, priv->d.pI2CBus->BusName, priv->d.SlaveAddr); 154fa225cbcSrjs } 155fa225cbcSrjs 156fa225cbcSrjs return FALSE; 157fa225cbcSrjs} 158fa225cbcSrjs 159fa225cbcSrjs/** Probes the given bus and slave address for an ivch */ 160fa225cbcSrjsstatic void * 161fa225cbcSrjsivch_init(I2CBusPtr b, I2CSlaveAddr addr) 162fa225cbcSrjs{ 163fa225cbcSrjs struct ivch_priv *priv; 164fa225cbcSrjs uint16_t temp; 165fa225cbcSrjs 166fa225cbcSrjs priv = xcalloc(1, sizeof(struct ivch_priv)); 167fa225cbcSrjs if (priv == NULL) 168fa225cbcSrjs return NULL; 169fa225cbcSrjs 170fa225cbcSrjs priv->output = NULL; 171fa225cbcSrjs priv->d.DevName = "i82807aa \"ivch\" LVDS/CMOS panel controller"; 172fa225cbcSrjs priv->d.SlaveAddr = addr; 173fa225cbcSrjs priv->d.pI2CBus = b; 174fa225cbcSrjs priv->d.StartTimeout = b->StartTimeout; 175fa225cbcSrjs priv->d.BitTimeout = b->BitTimeout; 176fa225cbcSrjs priv->d.AcknTimeout = b->AcknTimeout; 177fa225cbcSrjs priv->d.ByteTimeout = b->ByteTimeout; 178fa225cbcSrjs priv->d.DriverPrivate.ptr = priv; 179fa225cbcSrjs priv->quiet = TRUE; 180fa225cbcSrjs 181fa225cbcSrjs if (!ivch_read(priv, VR00, &temp)) 182fa225cbcSrjs goto out; 183fa225cbcSrjs priv->quiet = FALSE; 184fa225cbcSrjs 185fa225cbcSrjs /* Since the identification bits are probably zeroes, which doesn't seem 186fa225cbcSrjs * very unique, check that the value in the base address field matches 187fa225cbcSrjs * the address it's responding on. 188fa225cbcSrjs */ 189fa225cbcSrjs if ((temp & VR00_BASE_ADDRESS_MASK) != (priv->d.SlaveAddr >> 1)) { 190fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR, 191fa225cbcSrjs "ivch detect failed due to address mismatch " 192fa225cbcSrjs "(%d vs %d)\n", 193fa225cbcSrjs (temp & VR00_BASE_ADDRESS_MASK), priv->d.SlaveAddr >> 1); 194fa225cbcSrjs goto out; 195fa225cbcSrjs } 196fa225cbcSrjs 197fa225cbcSrjs if (!xf86I2CDevInit(&priv->d)) { 198fa225cbcSrjs goto out; 199fa225cbcSrjs } 200fa225cbcSrjs 201fa225cbcSrjs ivch_read(priv, VR20, &priv->width); 202fa225cbcSrjs ivch_read(priv, VR21, &priv->height); 203fa225cbcSrjs 204fa225cbcSrjs return priv; 205fa225cbcSrjs 206fa225cbcSrjsout: 207fa225cbcSrjs xfree(priv); 208fa225cbcSrjs return NULL; 209fa225cbcSrjs} 210fa225cbcSrjs 211fa225cbcSrjsstatic xf86OutputStatus 212fa225cbcSrjsivch_detect(I2CDevPtr d) 213fa225cbcSrjs{ 214fa225cbcSrjs return XF86OutputStatusConnected; 215fa225cbcSrjs} 216fa225cbcSrjs 217fa225cbcSrjsstatic ModeStatus 218fa225cbcSrjsivch_mode_valid(I2CDevPtr d, DisplayModePtr mode) 219fa225cbcSrjs{ 220fa225cbcSrjs if (mode->Clock > 112000) 221fa225cbcSrjs return MODE_CLOCK_HIGH; 222fa225cbcSrjs 223fa225cbcSrjs return MODE_OK; 224fa225cbcSrjs} 225fa225cbcSrjs 226fa225cbcSrjs/** Sets the power state of the panel connected to the ivch */ 227fa225cbcSrjsstatic void 228fa225cbcSrjsivch_dpms(I2CDevPtr d, int mode) 229fa225cbcSrjs{ 230fa225cbcSrjs struct ivch_priv *priv = d->DriverPrivate.ptr; 231fa225cbcSrjs int i; 232fa225cbcSrjs uint16_t vr01, vr30, backlight; 233fa225cbcSrjs 234fa225cbcSrjs /* Set the new power state of the panel. */ 235fa225cbcSrjs if (!ivch_read(priv, VR01, &vr01)) 236fa225cbcSrjs return; 237fa225cbcSrjs 238fa225cbcSrjs if (mode == DPMSModeOn) 239fa225cbcSrjs backlight = 1; 240fa225cbcSrjs else 241fa225cbcSrjs backlight = 0; 242fa225cbcSrjs ivch_write(priv, VR80, backlight); 243fa225cbcSrjs 244fa225cbcSrjs if (mode == DPMSModeOn) 245fa225cbcSrjs vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; 246fa225cbcSrjs else 247fa225cbcSrjs vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); 248fa225cbcSrjs 249fa225cbcSrjs ivch_write(priv, VR01, vr01); 250fa225cbcSrjs 251fa225cbcSrjs /* Wait for the panel to make its state transition */ 252fa225cbcSrjs for (i = 0; i < 100; i++) { 253fa225cbcSrjs if (!ivch_read(priv, VR30, &vr30)) 254fa225cbcSrjs break; 255fa225cbcSrjs 256fa225cbcSrjs if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn)) 257fa225cbcSrjs break; 258fa225cbcSrjs usleep (1000); 259fa225cbcSrjs } 260fa225cbcSrjs /* And wait some more; without this, the vch fails to resync sometimes */ 261fa225cbcSrjs usleep (16 * 1000); 262fa225cbcSrjs} 263fa225cbcSrjs 264fa225cbcSrjsstatic void 265fa225cbcSrjsivch_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode) 266fa225cbcSrjs{ 267fa225cbcSrjs struct ivch_priv *priv = d->DriverPrivate.ptr; 268fa225cbcSrjs uint16_t vr40 = 0; 269fa225cbcSrjs uint16_t vr01; 270fa225cbcSrjs 271fa225cbcSrjs vr01 = 0; 272fa225cbcSrjs vr40 = (VR40_STALL_ENABLE | 273fa225cbcSrjs VR40_VERTICAL_INTERP_ENABLE | 274fa225cbcSrjs VR40_HORIZONTAL_INTERP_ENABLE); 275fa225cbcSrjs 276fa225cbcSrjs if (mode->HDisplay != adjusted_mode->HDisplay || 277fa225cbcSrjs mode->VDisplay != adjusted_mode->VDisplay) 278fa225cbcSrjs { 279fa225cbcSrjs uint16_t x_ratio, y_ratio; 280fa225cbcSrjs 281fa225cbcSrjs vr01 |= VR01_PANEL_FIT_ENABLE; 282fa225cbcSrjs vr40 |= VR40_CLOCK_GATING_ENABLE; 283fa225cbcSrjs x_ratio = (((mode->HDisplay - 1) << 16) / (adjusted_mode->HDisplay - 1)) >> 2; 284fa225cbcSrjs y_ratio = (((mode->VDisplay - 1) << 16) / (adjusted_mode->VDisplay - 1)) >> 2; 285fa225cbcSrjs ivch_write (priv, VR42, x_ratio); 286fa225cbcSrjs ivch_write (priv, VR41, y_ratio); 287fa225cbcSrjs } 288fa225cbcSrjs else 289fa225cbcSrjs { 290fa225cbcSrjs vr01 &= ~VR01_PANEL_FIT_ENABLE; 291fa225cbcSrjs vr40 &= ~VR40_CLOCK_GATING_ENABLE; 292fa225cbcSrjs } 293fa225cbcSrjs vr40 &= ~VR40_AUTO_RATIO_ENABLE; 294fa225cbcSrjs 295fa225cbcSrjs ivch_write(priv, VR01, vr01); 296fa225cbcSrjs ivch_write(priv, VR40, vr40); 297fa225cbcSrjs 298fa225cbcSrjs ivch_dump_regs(d); 299fa225cbcSrjs} 300fa225cbcSrjs 301fa225cbcSrjsstatic void 302fa225cbcSrjsivch_dump_regs(I2CDevPtr d) 303fa225cbcSrjs{ 304fa225cbcSrjs struct ivch_priv *priv = d->DriverPrivate.ptr; 305fa225cbcSrjs uint16_t val; 306fa225cbcSrjs 307fa225cbcSrjs ivch_read(priv, VR00, &val); 308fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR00: 0x%04x\n", val); 309fa225cbcSrjs ivch_read(priv, VR01, &val); 310fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR01: 0x%04x\n", val); 311fa225cbcSrjs ivch_read(priv, VR30, &val); 312fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR30: 0x%04x\n", val); 313fa225cbcSrjs ivch_read(priv, VR40, &val); 314fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR40: 0x%04x\n", val); 315fa225cbcSrjs 316fa225cbcSrjs /* GPIO registers */ 317fa225cbcSrjs ivch_read(priv, VR80, &val); 318fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR80: 0x%04x\n", val); 319fa225cbcSrjs ivch_read(priv, VR81, &val); 320fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR81: 0x%04x\n", val); 321fa225cbcSrjs ivch_read(priv, VR82, &val); 322fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR82: 0x%04x\n", val); 323fa225cbcSrjs ivch_read(priv, VR83, &val); 324fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR83: 0x%04x\n", val); 325fa225cbcSrjs ivch_read(priv, VR84, &val); 326fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR84: 0x%04x\n", val); 327fa225cbcSrjs ivch_read(priv, VR85, &val); 328fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR85: 0x%04x\n", val); 329fa225cbcSrjs ivch_read(priv, VR86, &val); 330fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR86: 0x%04x\n", val); 331fa225cbcSrjs ivch_read(priv, VR87, &val); 332fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR87: 0x%04x\n", val); 333fa225cbcSrjs ivch_read(priv, VR88, &val); 334fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR88: 0x%04x\n", val); 335fa225cbcSrjs 336fa225cbcSrjs /* Scratch register 0 - AIM Panel type */ 337fa225cbcSrjs ivch_read(priv, VR8E, &val); 338fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR8E: 0x%04x\n", val); 339fa225cbcSrjs 340fa225cbcSrjs /* Scratch register 1 - Status register */ 341fa225cbcSrjs ivch_read(priv, VR8F, &val); 342fa225cbcSrjs xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR8F: 0x%04x\n", val); 343fa225cbcSrjs} 344fa225cbcSrjs 345fa225cbcSrjsstatic void 346fa225cbcSrjsivch_save(I2CDevPtr d) 347fa225cbcSrjs{ 348fa225cbcSrjs struct ivch_priv *priv = d->DriverPrivate.ptr; 349fa225cbcSrjs 350fa225cbcSrjs ivch_read(priv, VR01, &priv->save_VR01); 351fa225cbcSrjs ivch_read(priv, VR40, &priv->save_VR40); 352fa225cbcSrjs} 353fa225cbcSrjs 354fa225cbcSrjsstatic void 355fa225cbcSrjsivch_restore(I2CDevPtr d) 356fa225cbcSrjs{ 357fa225cbcSrjs struct ivch_priv *priv = d->DriverPrivate.ptr; 358fa225cbcSrjs 359fa225cbcSrjs ivch_write(priv, VR01, priv->save_VR01); 360fa225cbcSrjs ivch_write(priv, VR40, priv->save_VR40); 361fa225cbcSrjs} 362fa225cbcSrjs 363fa225cbcSrjs 364fa225cbcSrjs_X_EXPORT I830I2CVidOutputRec ivch_methods = { 365fa225cbcSrjs .init = ivch_init, 366fa225cbcSrjs .dpms = ivch_dpms, 367fa225cbcSrjs .save = ivch_save, 368fa225cbcSrjs .restore = ivch_restore, 369fa225cbcSrjs .mode_valid = ivch_mode_valid, 370fa225cbcSrjs .mode_set = ivch_mode_set, 371fa225cbcSrjs .detect = ivch_detect, 372fa225cbcSrjs .dump_regs = ivch_dump_regs, 373fa225cbcSrjs}; 374