1 /************************************************************************** 2 3 Copyright © 2006 Dave Airlie 4 5 All Rights Reserved. 6 7 Permission is hereby granted, free of charge, to any person obtaining a 8 copy of this software and associated documentation files (the 9 "Software"), to deal in the Software without restriction, including 10 without limitation the rights to use, copy, modify, merge, publish, 11 distribute, sub license, and/or sell copies of the Software, and to 12 permit persons to whom the Software is furnished to do so, subject to 13 the following conditions: 14 15 The above copyright notice and this permission notice (including the 16 next paragraph) shall be included in all copies or substantial portions 17 of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 23 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 27 **************************************************************************/ 28 29 #include <drm/drm_print.h> 30 31 #include "intel_display_types.h" 32 #include "intel_dvo_dev.h" 33 34 #define CH7xxx_REG_VID 0x4a 35 #define CH7xxx_REG_DID 0x4b 36 37 #define CH7011_VID 0x83 /* 7010 as well */ 38 #define CH7010B_VID 0x05 39 #define CH7009A_VID 0x84 40 #define CH7009B_VID 0x85 41 #define CH7301_VID 0x95 42 43 #define CH7xxx_VID 0x84 44 #define CH7xxx_DID 0x17 45 #define CH7010_DID 0x16 46 47 #define CH7xxx_NUM_REGS 0x4c 48 49 #define CH7xxx_CM 0x1c 50 #define CH7xxx_CM_XCM (1<<0) 51 #define CH7xxx_CM_MCP (1<<2) 52 #define CH7xxx_INPUT_CLOCK 0x1d 53 #define CH7xxx_GPIO 0x1e 54 #define CH7xxx_GPIO_HPIR (1<<3) 55 56 #define CH7xxx_IDF 0x1f 57 #define CH7xxx_IDF_IBS (1<<7) 58 #define CH7xxx_IDF_DES (1<<6) 59 #define CH7xxx_IDF_HSP (1<<3) 60 #define CH7xxx_IDF_VSP (1<<4) 61 62 #define CH7xxx_CONNECTION_DETECT 0x20 63 #define CH7xxx_CDET_DVI (1<<5) 64 65 #define CH7xxx_DAC_CNTL 0x21 66 #define CH7xxx_SYNCO_MASK (3 << 3) 67 #define CH7xxx_SYNCO_VGA_HSYNC (1 << 3) 68 69 #define CH7xxx_CLOCK_OUTPUT 0x22 70 #define CH7xxx_BCOEN (1 << 4) 71 #define CH7xxx_BCOP (1 << 3) 72 #define CH7xxx_BCO_MASK (7 << 0) 73 #define CH7xxx_BCO_VGA_VSYNC (6 << 0) 74 75 #define CH7301_HOTPLUG 0x23 76 #define CH7xxx_TCTL 0x31 77 #define CH7xxx_TVCO 0x32 78 #define CH7xxx_TPCP 0x33 79 #define CH7xxx_TPD 0x34 80 #define CH7xxx_TPVT 0x35 81 #define CH7xxx_TLPF 0x36 82 #define CH7xxx_TCT 0x37 83 #define CH7301_TEST_PATTERN 0x48 84 85 #define CH7xxx_PM 0x49 86 #define CH7xxx_PM_FPD (1<<0) 87 #define CH7301_PM_DACPD0 (1<<1) 88 #define CH7301_PM_DACPD1 (1<<2) 89 #define CH7301_PM_DACPD2 (1<<3) 90 #define CH7xxx_PM_DVIL (1<<6) 91 #define CH7xxx_PM_DVIP (1<<7) 92 93 #define CH7301_SYNC_POLARITY 0x56 94 #define CH7301_SYNC_RGB_YUV (1<<0) 95 #define CH7301_SYNC_POL_DVI (1<<5) 96 97 /** @file 98 * driver for the Chrontel 7xxx DVI chip over DVO. 99 */ 100 101 static struct ch7xxx_id_struct { 102 u8 vid; 103 char *name; 104 } ch7xxx_ids[] = { 105 { CH7011_VID, "CH7011" }, 106 { CH7010B_VID, "CH7010B" }, 107 { CH7009A_VID, "CH7009A" }, 108 { CH7009B_VID, "CH7009B" }, 109 { CH7301_VID, "CH7301" }, 110 }; 111 112 static struct ch7xxx_did_struct { 113 u8 did; 114 char *name; 115 } ch7xxx_dids[] = { 116 { CH7xxx_DID, "CH7XXX" }, 117 { CH7010_DID, "CH7010B" }, 118 }; 119 120 struct ch7xxx_priv { 121 bool quiet; 122 }; 123 124 static char *ch7xxx_get_id(u8 vid) 125 { 126 int i; 127 128 for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) { 129 if (ch7xxx_ids[i].vid == vid) 130 return ch7xxx_ids[i].name; 131 } 132 133 return NULL; 134 } 135 136 static char *ch7xxx_get_did(u8 did) 137 { 138 int i; 139 140 for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) { 141 if (ch7xxx_dids[i].did == did) 142 return ch7xxx_dids[i].name; 143 } 144 145 return NULL; 146 } 147 148 /** Reads an 8 bit register */ 149 static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, u8 *ch) 150 { 151 struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 152 struct i2c_adapter *adapter = dvo->i2c_bus; 153 u8 out_buf[2]; 154 u8 in_buf[2]; 155 156 struct i2c_msg msgs[] = { 157 { 158 .addr = dvo->target_addr, 159 .flags = 0, 160 .len = 1, 161 .buf = out_buf, 162 }, 163 { 164 .addr = dvo->target_addr, 165 .flags = I2C_M_RD, 166 .len = 1, 167 .buf = in_buf, 168 } 169 }; 170 171 out_buf[0] = addr; 172 out_buf[1] = 0; 173 174 if (i2c_transfer(adapter, msgs, 2) == 2) { 175 *ch = in_buf[0]; 176 return true; 177 } 178 179 if (!ch7xxx->quiet) { 180 DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", 181 addr, adapter->name, dvo->target_addr); 182 } 183 return false; 184 } 185 186 /** Writes an 8 bit register */ 187 static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, u8 ch) 188 { 189 struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 190 struct i2c_adapter *adapter = dvo->i2c_bus; 191 u8 out_buf[2]; 192 struct i2c_msg msg = { 193 .addr = dvo->target_addr, 194 .flags = 0, 195 .len = 2, 196 .buf = out_buf, 197 }; 198 199 out_buf[0] = addr; 200 out_buf[1] = ch; 201 202 if (i2c_transfer(adapter, &msg, 1) == 1) 203 return true; 204 205 if (!ch7xxx->quiet) { 206 DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", 207 addr, adapter->name, dvo->target_addr); 208 } 209 210 return false; 211 } 212 213 static bool ch7xxx_init(struct intel_dvo_device *dvo, 214 struct i2c_adapter *adapter) 215 { 216 /* this will detect the CH7xxx chip on the specified i2c bus */ 217 struct ch7xxx_priv *ch7xxx; 218 u8 vendor, device; 219 char *name, *devid; 220 221 ch7xxx = kzalloc(sizeof(*ch7xxx), GFP_KERNEL); 222 if (ch7xxx == NULL) 223 return false; 224 225 dvo->i2c_bus = adapter; 226 dvo->dev_priv = ch7xxx; 227 ch7xxx->quiet = true; 228 229 if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor)) 230 goto out; 231 232 name = ch7xxx_get_id(vendor); 233 if (!name) { 234 DRM_DEBUG_KMS("ch7xxx not detected; got VID 0x%02x from %s target %d.\n", 235 vendor, adapter->name, dvo->target_addr); 236 goto out; 237 } 238 239 240 if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) 241 goto out; 242 243 devid = ch7xxx_get_did(device); 244 if (!devid) { 245 DRM_DEBUG_KMS("ch7xxx not detected; got DID 0x%02x from %s target %d.\n", 246 device, adapter->name, dvo->target_addr); 247 goto out; 248 } 249 250 ch7xxx->quiet = false; 251 DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", 252 name, vendor, device); 253 return true; 254 out: 255 kfree(ch7xxx); 256 return false; 257 } 258 259 static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo) 260 { 261 u8 cdet, orig_pm, pm; 262 263 ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm); 264 265 pm = orig_pm; 266 pm &= ~CH7xxx_PM_FPD; 267 pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; 268 269 ch7xxx_writeb(dvo, CH7xxx_PM, pm); 270 271 ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet); 272 273 ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm); 274 275 if (cdet & CH7xxx_CDET_DVI) 276 return connector_status_connected; 277 return connector_status_disconnected; 278 } 279 280 static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo, 281 const struct drm_display_mode *mode) 282 { 283 if (mode->clock > 165000) 284 return MODE_CLOCK_HIGH; 285 286 return MODE_OK; 287 } 288 289 static void ch7xxx_mode_set(struct intel_dvo_device *dvo, 290 const struct drm_display_mode *mode, 291 const struct drm_display_mode *adjusted_mode) 292 { 293 u8 tvco, tpcp, tpd, tlpf, idf; 294 295 if (mode->clock <= 65000) { 296 tvco = 0x23; 297 tpcp = 0x08; 298 tpd = 0x16; 299 tlpf = 0x60; 300 } else { 301 tvco = 0x2d; 302 tpcp = 0x06; 303 tpd = 0x26; 304 tlpf = 0xa0; 305 } 306 307 ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00); 308 ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco); 309 ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp); 310 ch7xxx_writeb(dvo, CH7xxx_TPD, tpd); 311 ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30); 312 ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf); 313 ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00); 314 315 ch7xxx_readb(dvo, CH7xxx_IDF, &idf); 316 317 idf |= CH7xxx_IDF_IBS; 318 319 idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); 320 if (mode->flags & DRM_MODE_FLAG_PHSYNC) 321 idf |= CH7xxx_IDF_HSP; 322 323 if (mode->flags & DRM_MODE_FLAG_PVSYNC) 324 idf |= CH7xxx_IDF_VSP; 325 326 ch7xxx_writeb(dvo, CH7xxx_IDF, idf); 327 328 ch7xxx_writeb(dvo, CH7xxx_DAC_CNTL, 329 CH7xxx_SYNCO_VGA_HSYNC); 330 ch7xxx_writeb(dvo, CH7xxx_CLOCK_OUTPUT, 331 CH7xxx_BCOEN | CH7xxx_BCO_VGA_VSYNC); 332 } 333 334 /* set the CH7xxx power state */ 335 static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) 336 { 337 if (enable) 338 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); 339 else 340 ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); 341 } 342 343 static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo) 344 { 345 u8 val; 346 347 ch7xxx_readb(dvo, CH7xxx_PM, &val); 348 349 if (val & (CH7xxx_PM_DVIL | CH7xxx_PM_DVIP)) 350 return true; 351 else 352 return false; 353 } 354 355 static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) 356 { 357 int i; 358 359 for (i = 0; i < CH7xxx_NUM_REGS; i++) { 360 u8 val; 361 if ((i % 8) == 0) 362 DRM_DEBUG_KMS("\n %02X: ", i); 363 ch7xxx_readb(dvo, i, &val); 364 DRM_DEBUG_KMS("%02X ", val); 365 } 366 } 367 368 static void ch7xxx_destroy(struct intel_dvo_device *dvo) 369 { 370 struct ch7xxx_priv *ch7xxx = dvo->dev_priv; 371 372 if (ch7xxx) { 373 kfree(ch7xxx); 374 dvo->dev_priv = NULL; 375 } 376 } 377 378 const struct intel_dvo_dev_ops ch7xxx_ops = { 379 .init = ch7xxx_init, 380 .detect = ch7xxx_detect, 381 .mode_valid = ch7xxx_mode_valid, 382 .mode_set = ch7xxx_mode_set, 383 .dpms = ch7xxx_dpms, 384 .get_hw_state = ch7xxx_get_hw_state, 385 .dump_regs = ch7xxx_dump_regs, 386 .destroy = ch7xxx_destroy, 387 }; 388