1/* 2 * Copyright �� 2006 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: 24 * Eric Anholt <eric@anholt.net> 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include "dvo.h" 32 33/* 34 * register definitions for the i82807aa. 35 * 36 * Documentation on this chipset can be found in datasheet #29069001 at 37 * intel.com. 38 */ 39 40/* 41 * VCH Revision & GMBus Base Addr 42 */ 43#define VR00 0x00 44# define VR00_BASE_ADDRESS_MASK 0x007f 45 46/* 47 * Functionality Enable 48 */ 49#define VR01 0x01 50 51/* 52 * Enable the panel fitter 53 */ 54# define VR01_PANEL_FIT_ENABLE (1 << 3) 55/* 56 * Enables the LCD display. 57 * 58 * This must not be set while VR01_DVO_BYPASS_ENABLE is set. 59 */ 60# define VR01_LCD_ENABLE (1 << 2) 61/** Enables the DVO repeater. */ 62# define VR01_DVO_BYPASS_ENABLE (1 << 1) 63/** Enables the DVO clock */ 64# define VR01_DVO_ENABLE (1 << 0) 65 66/* 67 * LCD Interface Format 68 */ 69#define VR10 0x10 70/** Enables LVDS output instead of CMOS */ 71# define VR10_LVDS_ENABLE (1 << 4) 72/** Enables 18-bit LVDS output. */ 73# define VR10_INTERFACE_1X18 (0 << 2) 74/** Enables 24-bit LVDS or CMOS output */ 75# define VR10_INTERFACE_1X24 (1 << 2) 76/** Enables 2x18-bit LVDS or CMOS output. */ 77# define VR10_INTERFACE_2X18 (2 << 2) 78/** Enables 2x24-bit LVDS output */ 79# define VR10_INTERFACE_2X24 (3 << 2) 80 81/* 82 * VR20 LCD Horizontal Display Size 83 */ 84#define VR20 0x20 85 86/* 87 * LCD Vertical Display Size 88 */ 89#define VR21 0x20 90 91/* 92 * Panel power down status 93 */ 94#define VR30 0x30 95/** Read only bit indicating that the panel is not in a safe poweroff state. */ 96# define VR30_PANEL_ON (1 << 15) 97 98#define VR40 0x40 99# define VR40_STALL_ENABLE (1 << 13) 100# define VR40_VERTICAL_INTERP_ENABLE (1 << 12) 101# define VR40_ENHANCED_PANEL_FITTING (1 << 11) 102# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10) 103# define VR40_AUTO_RATIO_ENABLE (1 << 9) 104# define VR40_CLOCK_GATING_ENABLE (1 << 8) 105 106/* 107 * Panel Fitting Vertical Ratio 108 * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2 109 */ 110#define VR41 0x41 111 112/* 113 * Panel Fitting Horizontal Ratio 114 * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2 115 */ 116#define VR42 0x42 117 118/* 119 * Horizontal Image Size 120 */ 121#define VR43 0x43 122 123/* VR80 GPIO 0 124 */ 125#define VR80 0x80 126#define VR81 0x81 127#define VR82 0x82 128#define VR83 0x83 129#define VR84 0x84 130#define VR85 0x85 131#define VR86 0x86 132#define VR87 0x87 133 134/* VR88 GPIO 8 135 */ 136#define VR88 0x88 137 138/* Graphics BIOS scratch 0 139 */ 140#define VR8E 0x8E 141# define VR8E_PANEL_TYPE_MASK (0xf << 0) 142# define VR8E_PANEL_INTERFACE_CMOS (0 << 4) 143# define VR8E_PANEL_INTERFACE_LVDS (1 << 4) 144# define VR8E_FORCE_DEFAULT_PANEL (1 << 5) 145 146/* Graphics BIOS scratch 1 147 */ 148#define VR8F 0x8F 149# define VR8F_VCH_PRESENT (1 << 0) 150# define VR8F_DISPLAY_CONN (1 << 1) 151# define VR8F_POWER_MASK (0x3c) 152# define VR8F_POWER_POS (2) 153 154 155struct ivch_priv { 156 bool quiet; 157 158 uint16_t width, height; 159}; 160 161 162static void ivch_dump_regs(struct intel_dvo_device *dvo); 163 164/** 165 * Reads a register on the ivch. 166 * 167 * Each of the 256 registers are 16 bits long. 168 */ 169static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) 170{ 171 struct ivch_priv *priv = dvo->dev_priv; 172 device_t adapter = dvo->i2c_bus; 173 u8 out_buf[1]; 174 u8 in_buf[2]; 175 176 struct iic_msg msgs[] = { 177 { 178 .slave = dvo->slave_addr << 1, 179 .flags = I2C_M_RD, 180 .len = 0, 181 }, 182 { 183 .slave = 0 << 1, 184 .flags = I2C_M_NOSTART, 185 .len = 1, 186 .buf = out_buf, 187 }, 188 { 189 .slave = dvo->slave_addr << 1, 190 .flags = I2C_M_RD | I2C_M_NOSTART, 191 .len = 2, 192 .buf = in_buf, 193 } 194 }; 195 196 out_buf[0] = addr; 197 198 if (-iicbus_transfer(adapter, msgs, 3) == 0) { 199 *data = (in_buf[1] << 8) | in_buf[0]; 200 return true; 201 } 202 203 if (!priv->quiet) { 204 DRM_DEBUG_KMS("Unable to read register 0x%02x from " 205 "%s:%02x.\n", 206 addr, device_get_nameunit(adapter), dvo->slave_addr); 207 } 208 return false; 209} 210 211/** Writes a 16-bit register on the ivch */ 212static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) 213{ 214 struct ivch_priv *priv = dvo->dev_priv; 215 device_t adapter = dvo->i2c_bus; 216 u8 out_buf[3]; 217 struct iic_msg msg = { 218 .slave = dvo->slave_addr << 1, 219 .flags = 0, 220 .len = 3, 221 .buf = out_buf, 222 }; 223 224 out_buf[0] = addr; 225 out_buf[1] = data & 0xff; 226 out_buf[2] = data >> 8; 227 228 if (-iicbus_transfer(adapter, &msg, 1) == 0) 229 return true; 230 231 if (!priv->quiet) { 232 DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", 233 addr, device_get_nameunit(adapter), dvo->slave_addr); 234 } 235 236 return false; 237} 238 239/** Probes the given bus and slave address for an ivch */ 240static bool ivch_init(struct intel_dvo_device *dvo, 241 device_t adapter) 242{ 243 struct ivch_priv *priv; 244 uint16_t temp; 245 246 priv = malloc(sizeof(struct ivch_priv), DRM_MEM_KMS, M_NOWAIT | M_ZERO); 247 if (priv == NULL) 248 return false; 249 250 dvo->i2c_bus = adapter; 251 dvo->dev_priv = priv; 252 priv->quiet = true; 253 254 if (!ivch_read(dvo, VR00, &temp)) 255 goto out; 256 priv->quiet = false; 257 258 /* Since the identification bits are probably zeroes, which doesn't seem 259 * very unique, check that the value in the base address field matches 260 * the address it's responding on. 261 */ 262 if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) { 263 DRM_DEBUG_KMS("ivch detect failed due to address mismatch " 264 "(%d vs %d)\n", 265 (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr); 266 goto out; 267 } 268 269 ivch_read(dvo, VR20, &priv->width); 270 ivch_read(dvo, VR21, &priv->height); 271 272 return true; 273 274out: 275 free(priv, DRM_MEM_KMS); 276 return false; 277} 278 279static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo) 280{ 281 return connector_status_connected; 282} 283 284static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, 285 struct drm_display_mode *mode) 286{ 287 if (mode->clock > 112000) 288 return MODE_CLOCK_HIGH; 289 290 return MODE_OK; 291} 292 293/** Sets the power state of the panel connected to the ivch */ 294static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) 295{ 296 int i; 297 uint16_t vr01, vr30, backlight; 298 299 /* Set the new power state of the panel. */ 300 if (!ivch_read(dvo, VR01, &vr01)) 301 return; 302 303 if (enable) 304 backlight = 1; 305 else 306 backlight = 0; 307 ivch_write(dvo, VR80, backlight); 308 309 if (enable) 310 vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; 311 else 312 vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); 313 314 ivch_write(dvo, VR01, vr01); 315 316 /* Wait for the panel to make its state transition */ 317 for (i = 0; i < 100; i++) { 318 if (!ivch_read(dvo, VR30, &vr30)) 319 break; 320 321 if (((vr30 & VR30_PANEL_ON) != 0) == enable) 322 break; 323 udelay(1000); 324 } 325 /* wait some more; vch may fail to resync sometimes without this */ 326 udelay(16 * 1000); 327} 328 329static bool ivch_get_hw_state(struct intel_dvo_device *dvo) 330{ 331 uint16_t vr01; 332 333 /* Set the new power state of the panel. */ 334 if (!ivch_read(dvo, VR01, &vr01)) 335 return false; 336 337 if (vr01 & VR01_LCD_ENABLE) 338 return true; 339 else 340 return false; 341} 342 343static void ivch_mode_set(struct intel_dvo_device *dvo, 344 struct drm_display_mode *mode, 345 struct drm_display_mode *adjusted_mode) 346{ 347 uint16_t vr40 = 0; 348 uint16_t vr01; 349 350 vr01 = 0; 351 vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE | 352 VR40_HORIZONTAL_INTERP_ENABLE); 353 354 if (mode->hdisplay != adjusted_mode->hdisplay || 355 mode->vdisplay != adjusted_mode->vdisplay) { 356 uint16_t x_ratio, y_ratio; 357 358 vr01 |= VR01_PANEL_FIT_ENABLE; 359 vr40 |= VR40_CLOCK_GATING_ENABLE; 360 x_ratio = (((mode->hdisplay - 1) << 16) / 361 (adjusted_mode->hdisplay - 1)) >> 2; 362 y_ratio = (((mode->vdisplay - 1) << 16) / 363 (adjusted_mode->vdisplay - 1)) >> 2; 364 ivch_write(dvo, VR42, x_ratio); 365 ivch_write(dvo, VR41, y_ratio); 366 } else { 367 vr01 &= ~VR01_PANEL_FIT_ENABLE; 368 vr40 &= ~VR40_CLOCK_GATING_ENABLE; 369 } 370 vr40 &= ~VR40_AUTO_RATIO_ENABLE; 371 372 ivch_write(dvo, VR01, vr01); 373 ivch_write(dvo, VR40, vr40); 374 375 ivch_dump_regs(dvo); 376} 377 378static void ivch_dump_regs(struct intel_dvo_device *dvo) 379{ 380 uint16_t val; 381 382 ivch_read(dvo, VR00, &val); 383 DRM_LOG_KMS("VR00: 0x%04x\n", val); 384 ivch_read(dvo, VR01, &val); 385 DRM_LOG_KMS("VR01: 0x%04x\n", val); 386 ivch_read(dvo, VR30, &val); 387 DRM_LOG_KMS("VR30: 0x%04x\n", val); 388 ivch_read(dvo, VR40, &val); 389 DRM_LOG_KMS("VR40: 0x%04x\n", val); 390 391 /* GPIO registers */ 392 ivch_read(dvo, VR80, &val); 393 DRM_LOG_KMS("VR80: 0x%04x\n", val); 394 ivch_read(dvo, VR81, &val); 395 DRM_LOG_KMS("VR81: 0x%04x\n", val); 396 ivch_read(dvo, VR82, &val); 397 DRM_LOG_KMS("VR82: 0x%04x\n", val); 398 ivch_read(dvo, VR83, &val); 399 DRM_LOG_KMS("VR83: 0x%04x\n", val); 400 ivch_read(dvo, VR84, &val); 401 DRM_LOG_KMS("VR84: 0x%04x\n", val); 402 ivch_read(dvo, VR85, &val); 403 DRM_LOG_KMS("VR85: 0x%04x\n", val); 404 ivch_read(dvo, VR86, &val); 405 DRM_LOG_KMS("VR86: 0x%04x\n", val); 406 ivch_read(dvo, VR87, &val); 407 DRM_LOG_KMS("VR87: 0x%04x\n", val); 408 ivch_read(dvo, VR88, &val); 409 DRM_LOG_KMS("VR88: 0x%04x\n", val); 410 411 /* Scratch register 0 - AIM Panel type */ 412 ivch_read(dvo, VR8E, &val); 413 DRM_LOG_KMS("VR8E: 0x%04x\n", val); 414 415 /* Scratch register 1 - Status register */ 416 ivch_read(dvo, VR8F, &val); 417 DRM_LOG_KMS("VR8F: 0x%04x\n", val); 418} 419 420static void ivch_destroy(struct intel_dvo_device *dvo) 421{ 422 struct ivch_priv *priv = dvo->dev_priv; 423 424 if (priv) { 425 free(priv, DRM_MEM_KMS); 426 dvo->dev_priv = NULL; 427 } 428} 429 430struct intel_dvo_dev_ops ivch_ops = { 431 .init = ivch_init, 432 .dpms = ivch_dpms, 433 .get_hw_state = ivch_get_hw_state, 434 .mode_valid = ivch_mode_valid, 435 .mode_set = ivch_mode_set, 436 .detect = ivch_detect, 437 .dump_regs = ivch_dump_regs, 438 .destroy = ivch_destroy, 439}; 440