1/************************************************************************** 2 3Copyright © 2006 Dave Airlie 4 5All Rights Reserved. 6 7Permission is hereby granted, free of charge, to any person obtaining a 8copy of this software and associated documentation files (the 9"Software"), to deal in the Software without restriction, including 10without limitation the rights to use, copy, modify, merge, publish, 11distribute, sub license, and/or sell copies of the Software, and to 12permit persons to whom the Software is furnished to do so, subject to 13the following conditions: 14 15The above copyright notice and this permission notice (including the 16next paragraph) shall be included in all copies or substantial portions 17of the Software. 18 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 27**************************************************************************/ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#include <stdint.h> 34#include <string.h> 35#include "xf86.h" 36#include "xf86_OSproc.h" 37#include "compiler.h" 38#include "miscstruct.h" 39#include "xf86i2c.h" 40#include "xf86Crtc.h" 41#ifdef HAVE_XEXTPROTO_71 42#include <X11/extensions/dpmsconst.h> 43#else 44#define DPMS_SERVER 45#include <X11/extensions/dpms.h> 46#endif 47 48 49#include "../i2c_vid.h" 50#include "ch7xxx.h" 51#include "ch7xxx_reg.h" 52 53/** @file 54 * driver for the Chrontel 7xxx DVI chip over DVO. 55 */ 56 57static struct ch7xxx_id_struct { 58 uint8_t vid; 59 char *name; 60} ch7xxx_ids[] = { 61 { CH7011_VID, "CH7011" }, 62 { CH7009A_VID, "CH7009A" }, 63 { CH7009B_VID, "CH7009B" }, 64 { CH7301_VID, "CH7301" }, 65}; 66 67#define ID_ARRAY_SIZE (sizeof(ch7xxx_ids) / sizeof(ch7xxx_ids[0])) 68 69struct ch7xxx_reg_state { 70 uint8_t regs[CH7xxx_NUM_REGS]; 71}; 72 73struct ch7xxx_priv { 74 I2CDevRec d; 75 Bool quiet; 76 77 struct ch7xxx_reg_state SavedReg; 78 struct ch7xxx_reg_state ModeReg; 79 uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT; 80 uint8_t save_TLPF, save_TCT, save_PM, save_IDF; 81}; 82 83static void ch7xxx_save(I2CDevPtr d); 84 85static char *ch7xxx_get_id(uint8_t vid) 86{ 87 int i; 88 89 for (i = 0; i < ID_ARRAY_SIZE; i++) { 90 if (ch7xxx_ids[i].vid == vid) 91 return ch7xxx_ids[i].name; 92 } 93 94 return NULL; 95} 96 97/** Reads an 8 bit register */ 98static Bool 99ch7xxx_read(struct ch7xxx_priv *dev_priv, int addr, unsigned char *ch) 100{ 101 if (!xf86I2CReadByte(&dev_priv->d, addr, ch)) { 102 if (!dev_priv->quiet) { 103 xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, 104 X_ERROR, "Unable to read from %s Slave %d.\n", 105 dev_priv->d.pI2CBus->BusName, dev_priv->d.SlaveAddr); 106 } 107 return FALSE; 108 } 109 110 return TRUE; 111} 112 113/** Writes an 8 bit register */ 114static Bool 115ch7xxx_write(struct ch7xxx_priv *dev_priv, int addr, unsigned char ch) 116{ 117 if (!xf86I2CWriteByte(&dev_priv->d, addr, ch)) { 118 if (!dev_priv->quiet) { 119 xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_ERROR, 120 "Unable to write to %s Slave %d.\n", 121 dev_priv->d.pI2CBus->BusName, dev_priv->d.SlaveAddr); 122 } 123 return FALSE; 124 } 125 126 return TRUE; 127} 128 129static void * 130ch7xxx_init(I2CBusPtr b, I2CSlaveAddr addr) 131{ 132 /* this will detect the CH7xxx chip on the specified i2c bus */ 133 struct ch7xxx_priv *dev_priv; 134 uint8_t vendor, device; 135 char *name; 136 137 dev_priv = xcalloc(1, sizeof(struct ch7xxx_priv)); 138 if (dev_priv == NULL) 139 return NULL; 140 141 dev_priv->d.DevName = "CH7xxx TMDS Controller"; 142 dev_priv->d.SlaveAddr = addr; 143 dev_priv->d.pI2CBus = b; 144 dev_priv->d.StartTimeout = b->StartTimeout; 145 dev_priv->d.BitTimeout = b->BitTimeout; 146 dev_priv->d.AcknTimeout = b->AcknTimeout; 147 dev_priv->d.ByteTimeout = b->ByteTimeout; 148 dev_priv->d.DriverPrivate.ptr = dev_priv; 149 150 dev_priv->quiet = TRUE; 151 if (!ch7xxx_read(dev_priv, CH7xxx_REG_VID, &vendor)) 152 goto out; 153 154 name = ch7xxx_get_id(vendor); 155 if (!name) { 156 xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO, 157 "ch7xxx not detected; got 0x%02x from %s slave %d.\n", 158 vendor, dev_priv->d.pI2CBus->BusName, 159 dev_priv->d.SlaveAddr); 160 goto out; 161 } 162 163 164 if (!ch7xxx_read(dev_priv, CH7xxx_REG_DID, &device)) 165 goto out; 166 167 if (device != CH7xxx_DID) { 168 xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO, 169 "ch7xxx not detected; got 0x%02x from %s slave %d.\n", 170 device, dev_priv->d.pI2CBus->BusName, 171 dev_priv->d.SlaveAddr); 172 goto out; 173 } 174 dev_priv->quiet = FALSE; 175 176 xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO, 177 "Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", 178 name, vendor, device); 179 180 if (!xf86I2CDevInit(&dev_priv->d)) { 181 goto out; 182 } 183 184 return dev_priv; 185 186out: 187 xfree(dev_priv); 188 return NULL; 189} 190 191static xf86OutputStatus 192ch7xxx_detect(I2CDevPtr d) 193{ 194 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 195 uint8_t cdet, orig_pm, pm; 196 197 ch7xxx_read(dev_priv, CH7xxx_PM, &orig_pm); 198 199 pm = orig_pm; 200 pm &= ~CH7xxx_PM_FPD; 201 pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; 202 203 ch7xxx_write(dev_priv, CH7xxx_PM, pm); 204 205 ch7xxx_read(dev_priv, CH7xxx_CONNECTION_DETECT, &cdet); 206 207 ch7xxx_write(dev_priv, CH7xxx_PM, orig_pm); 208 209 if (cdet & CH7xxx_CDET_DVI) 210 return XF86OutputStatusConnected; 211 return XF86OutputStatusDisconnected; 212} 213 214static ModeStatus 215ch7xxx_mode_valid(I2CDevPtr d, DisplayModePtr mode) 216{ 217 if (mode->Clock > 165000) 218 return MODE_CLOCK_HIGH; 219 220 return MODE_OK; 221} 222 223static void 224ch7xxx_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode) 225{ 226 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 227 uint8_t tvco, tpcp, tpd, tlpf, idf; 228 229 if (mode->Clock <= 65000) { 230 tvco = 0x23; 231 tpcp = 0x08; 232 tpd = 0x16; 233 tlpf = 0x60; 234 } else { 235 tvco = 0x2d; 236 tpcp = 0x06; 237 tpd = 0x26; 238 tlpf = 0xa0; 239 } 240 241 ch7xxx_write(dev_priv, CH7xxx_TCTL, 0x00); 242 ch7xxx_write(dev_priv, CH7xxx_TVCO, tvco); 243 ch7xxx_write(dev_priv, CH7xxx_TPCP, tpcp); 244 ch7xxx_write(dev_priv, CH7xxx_TPD, tpd); 245 ch7xxx_write(dev_priv, CH7xxx_TPVT, 0x30); 246 ch7xxx_write(dev_priv, CH7xxx_TLPF, tlpf); 247 ch7xxx_write(dev_priv, CH7xxx_TCT, 0x00); 248 249 ch7xxx_read(dev_priv, CH7xxx_IDF, &idf); 250 251 idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); 252 if (mode->Flags & V_PHSYNC) 253 idf |= CH7xxx_IDF_HSP; 254 255 if (mode->Flags & V_PVSYNC) 256 idf |= CH7xxx_IDF_HSP; 257 258 ch7xxx_write(dev_priv, CH7xxx_IDF, idf); 259} 260 261/* set the CH7xxx power state */ 262static void 263ch7xxx_dpms(I2CDevPtr d, int mode) 264{ 265 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 266 267 if (mode == DPMSModeOn) 268 ch7xxx_write(dev_priv, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); 269 else 270 ch7xxx_write(dev_priv, CH7xxx_PM, CH7xxx_PM_FPD); 271} 272 273static void 274ch7xxx_dump_regs(I2CDevPtr d) 275{ 276 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 277 int i; 278 279 for (i = 0; i < CH7xxx_NUM_REGS; i++) { 280 if (( i % 8 ) == 0 ) 281 ErrorF("\n %02X: ", i); 282 ErrorF("%02X ", dev_priv->ModeReg.regs[i]); 283 } 284} 285 286static void 287ch7xxx_save(I2CDevPtr d) 288{ 289 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 290 291 ch7xxx_read(dev_priv, CH7xxx_TCTL, &dev_priv->save_TCTL); 292 ch7xxx_read(dev_priv, CH7xxx_TPCP, &dev_priv->save_TPCP); 293 ch7xxx_read(dev_priv, CH7xxx_TPD, &dev_priv->save_TPD); 294 ch7xxx_read(dev_priv, CH7xxx_TPVT, &dev_priv->save_TPVT); 295 ch7xxx_read(dev_priv, CH7xxx_TLPF, &dev_priv->save_TLPF); 296 ch7xxx_read(dev_priv, CH7xxx_PM, &dev_priv->save_PM); 297 ch7xxx_read(dev_priv, CH7xxx_IDF, &dev_priv->save_IDF); 298} 299 300static void 301ch7xxx_restore(I2CDevPtr d) 302{ 303 struct ch7xxx_priv *dev_priv = d->DriverPrivate.ptr; 304 305 ch7xxx_write(dev_priv, CH7xxx_TCTL, dev_priv->save_TCTL); 306 ch7xxx_write(dev_priv, CH7xxx_TPCP, dev_priv->save_TPCP); 307 ch7xxx_write(dev_priv, CH7xxx_TPD, dev_priv->save_TPD); 308 ch7xxx_write(dev_priv, CH7xxx_TPVT, dev_priv->save_TPVT); 309 ch7xxx_write(dev_priv, CH7xxx_TLPF, dev_priv->save_TLPF); 310 ch7xxx_write(dev_priv, CH7xxx_IDF, dev_priv->save_IDF); 311 ch7xxx_write(dev_priv, CH7xxx_PM, dev_priv->save_PM); 312} 313 314_X_EXPORT I830I2CVidOutputRec CH7xxxVidOutput = { 315 .init = ch7xxx_init, 316 .detect = ch7xxx_detect, 317 .mode_valid = ch7xxx_mode_valid, 318 .mode_set = ch7xxx_mode_set, 319 .dpms = ch7xxx_dpms, 320 .dump_regs = ch7xxx_dump_regs, 321 .save = ch7xxx_save, 322 .restore = ch7xxx_restore, 323}; 324