0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 #include "intel_display_types.h"
0033 #include "intel_dvo_dev.h"
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 #define VR00 0x00
0046 # define VR00_BASE_ADDRESS_MASK 0x007f
0047
0048
0049
0050
0051 #define VR01 0x01
0052
0053
0054
0055
0056 # define VR01_PANEL_FIT_ENABLE (1 << 3)
0057
0058
0059
0060
0061
0062 # define VR01_LCD_ENABLE (1 << 2)
0063
0064 # define VR01_DVO_BYPASS_ENABLE (1 << 1)
0065
0066 # define VR01_DVO_ENABLE (1 << 0)
0067
0068 # define VR01_DITHER_ENABLE (1 << 4)
0069
0070
0071
0072
0073 #define VR10 0x10
0074
0075 # define VR10_LVDS_ENABLE (1 << 4)
0076
0077 # define VR10_INTERFACE_1X18 (0 << 2)
0078
0079 # define VR10_INTERFACE_1X24 (1 << 2)
0080
0081 # define VR10_INTERFACE_2X18 (2 << 2)
0082
0083 # define VR10_INTERFACE_2X24 (3 << 2)
0084
0085 # define VR10_INTERFACE_DEPTH_MASK (3 << 2)
0086
0087
0088
0089
0090 #define VR20 0x20
0091
0092
0093
0094
0095 #define VR21 0x21
0096
0097
0098
0099
0100 #define VR30 0x30
0101
0102 # define VR30_PANEL_ON (1 << 15)
0103
0104 #define VR40 0x40
0105 # define VR40_STALL_ENABLE (1 << 13)
0106 # define VR40_VERTICAL_INTERP_ENABLE (1 << 12)
0107 # define VR40_ENHANCED_PANEL_FITTING (1 << 11)
0108 # define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
0109 # define VR40_AUTO_RATIO_ENABLE (1 << 9)
0110 # define VR40_CLOCK_GATING_ENABLE (1 << 8)
0111
0112
0113
0114
0115
0116 #define VR41 0x41
0117
0118
0119
0120
0121
0122 #define VR42 0x42
0123
0124
0125
0126
0127 #define VR43 0x43
0128
0129
0130
0131 #define VR80 0x80
0132 #define VR81 0x81
0133 #define VR82 0x82
0134 #define VR83 0x83
0135 #define VR84 0x84
0136 #define VR85 0x85
0137 #define VR86 0x86
0138 #define VR87 0x87
0139
0140
0141
0142 #define VR88 0x88
0143
0144
0145
0146 #define VR8E 0x8E
0147 # define VR8E_PANEL_TYPE_MASK (0xf << 0)
0148 # define VR8E_PANEL_INTERFACE_CMOS (0 << 4)
0149 # define VR8E_PANEL_INTERFACE_LVDS (1 << 4)
0150 # define VR8E_FORCE_DEFAULT_PANEL (1 << 5)
0151
0152
0153
0154 #define VR8F 0x8F
0155 # define VR8F_VCH_PRESENT (1 << 0)
0156 # define VR8F_DISPLAY_CONN (1 << 1)
0157 # define VR8F_POWER_MASK (0x3c)
0158 # define VR8F_POWER_POS (2)
0159
0160
0161
0162
0163
0164
0165 static const u16 backup_addresses[] = {
0166 0x11, 0x12,
0167 0x18, 0x19, 0x1a, 0x1f,
0168 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0169 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0170 0x8e, 0x8f,
0171 0x10
0172 };
0173
0174
0175 struct ivch_priv {
0176 bool quiet;
0177
0178 u16 width, height;
0179
0180
0181
0182 u16 reg_backup[ARRAY_SIZE(backup_addresses)];
0183 };
0184
0185
0186 static void ivch_dump_regs(struct intel_dvo_device *dvo);
0187
0188
0189
0190
0191
0192 static bool ivch_read(struct intel_dvo_device *dvo, int addr, u16 *data)
0193 {
0194 struct ivch_priv *priv = dvo->dev_priv;
0195 struct i2c_adapter *adapter = dvo->i2c_bus;
0196 u8 out_buf[1];
0197 u8 in_buf[2];
0198
0199 struct i2c_msg msgs[] = {
0200 {
0201 .addr = dvo->slave_addr,
0202 .flags = I2C_M_RD,
0203 .len = 0,
0204 },
0205 {
0206 .addr = 0,
0207 .flags = I2C_M_NOSTART,
0208 .len = 1,
0209 .buf = out_buf,
0210 },
0211 {
0212 .addr = dvo->slave_addr,
0213 .flags = I2C_M_RD | I2C_M_NOSTART,
0214 .len = 2,
0215 .buf = in_buf,
0216 }
0217 };
0218
0219 out_buf[0] = addr;
0220
0221 if (i2c_transfer(adapter, msgs, 3) == 3) {
0222 *data = (in_buf[1] << 8) | in_buf[0];
0223 return true;
0224 }
0225
0226 if (!priv->quiet) {
0227 DRM_DEBUG_KMS("Unable to read register 0x%02x from "
0228 "%s:%02x.\n",
0229 addr, adapter->name, dvo->slave_addr);
0230 }
0231 return false;
0232 }
0233
0234
0235 static bool ivch_write(struct intel_dvo_device *dvo, int addr, u16 data)
0236 {
0237 struct ivch_priv *priv = dvo->dev_priv;
0238 struct i2c_adapter *adapter = dvo->i2c_bus;
0239 u8 out_buf[3];
0240 struct i2c_msg msg = {
0241 .addr = dvo->slave_addr,
0242 .flags = 0,
0243 .len = 3,
0244 .buf = out_buf,
0245 };
0246
0247 out_buf[0] = addr;
0248 out_buf[1] = data & 0xff;
0249 out_buf[2] = data >> 8;
0250
0251 if (i2c_transfer(adapter, &msg, 1) == 1)
0252 return true;
0253
0254 if (!priv->quiet) {
0255 DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
0256 addr, adapter->name, dvo->slave_addr);
0257 }
0258
0259 return false;
0260 }
0261
0262
0263 static bool ivch_init(struct intel_dvo_device *dvo,
0264 struct i2c_adapter *adapter)
0265 {
0266 struct ivch_priv *priv;
0267 u16 temp;
0268 int i;
0269
0270 priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
0271 if (priv == NULL)
0272 return false;
0273
0274 dvo->i2c_bus = adapter;
0275 dvo->dev_priv = priv;
0276 priv->quiet = true;
0277
0278 if (!ivch_read(dvo, VR00, &temp))
0279 goto out;
0280 priv->quiet = false;
0281
0282
0283
0284
0285
0286 if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
0287 DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
0288 "(%d vs %d)\n",
0289 (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
0290 goto out;
0291 }
0292
0293 ivch_read(dvo, VR20, &priv->width);
0294 ivch_read(dvo, VR21, &priv->height);
0295
0296
0297
0298
0299 for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
0300 ivch_read(dvo, backup_addresses[i], priv->reg_backup + i);
0301
0302 ivch_dump_regs(dvo);
0303
0304 return true;
0305
0306 out:
0307 kfree(priv);
0308 return false;
0309 }
0310
0311 static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
0312 {
0313 return connector_status_connected;
0314 }
0315
0316 static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
0317 struct drm_display_mode *mode)
0318 {
0319 if (mode->clock > 112000)
0320 return MODE_CLOCK_HIGH;
0321
0322 return MODE_OK;
0323 }
0324
0325
0326
0327
0328
0329 static void ivch_reset(struct intel_dvo_device *dvo)
0330 {
0331 struct ivch_priv *priv = dvo->dev_priv;
0332 int i;
0333
0334 DRM_DEBUG_KMS("Resetting the IVCH registers\n");
0335
0336 ivch_write(dvo, VR10, 0x0000);
0337
0338 for (i = 0; i < ARRAY_SIZE(backup_addresses); i++)
0339 ivch_write(dvo, backup_addresses[i], priv->reg_backup[i]);
0340 }
0341
0342
0343 static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
0344 {
0345 int i;
0346 u16 vr01, vr30, backlight;
0347
0348 ivch_reset(dvo);
0349
0350
0351 if (!ivch_read(dvo, VR01, &vr01))
0352 return;
0353
0354 if (enable)
0355 backlight = 1;
0356 else
0357 backlight = 0;
0358
0359 ivch_write(dvo, VR80, backlight);
0360
0361 if (enable)
0362 vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
0363 else
0364 vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
0365
0366 ivch_write(dvo, VR01, vr01);
0367
0368
0369 for (i = 0; i < 100; i++) {
0370 if (!ivch_read(dvo, VR30, &vr30))
0371 break;
0372
0373 if (((vr30 & VR30_PANEL_ON) != 0) == enable)
0374 break;
0375 udelay(1000);
0376 }
0377
0378 udelay(16 * 1000);
0379 }
0380
0381 static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
0382 {
0383 u16 vr01;
0384
0385 ivch_reset(dvo);
0386
0387
0388 if (!ivch_read(dvo, VR01, &vr01))
0389 return false;
0390
0391 if (vr01 & VR01_LCD_ENABLE)
0392 return true;
0393 else
0394 return false;
0395 }
0396
0397 static void ivch_mode_set(struct intel_dvo_device *dvo,
0398 const struct drm_display_mode *mode,
0399 const struct drm_display_mode *adjusted_mode)
0400 {
0401 struct ivch_priv *priv = dvo->dev_priv;
0402 u16 vr40 = 0;
0403 u16 vr01 = 0;
0404 u16 vr10;
0405
0406 ivch_reset(dvo);
0407
0408 vr10 = priv->reg_backup[ARRAY_SIZE(backup_addresses) - 1];
0409
0410
0411 vr10 &= VR10_INTERFACE_DEPTH_MASK;
0412 if (vr10 == VR10_INTERFACE_2X18 || vr10 == VR10_INTERFACE_1X18)
0413 vr01 = VR01_DITHER_ENABLE;
0414
0415 vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
0416 VR40_HORIZONTAL_INTERP_ENABLE);
0417
0418 if (mode->hdisplay != adjusted_mode->crtc_hdisplay ||
0419 mode->vdisplay != adjusted_mode->crtc_vdisplay) {
0420 u16 x_ratio, y_ratio;
0421
0422 vr01 |= VR01_PANEL_FIT_ENABLE;
0423 vr40 |= VR40_CLOCK_GATING_ENABLE;
0424 x_ratio = (((mode->hdisplay - 1) << 16) /
0425 (adjusted_mode->crtc_hdisplay - 1)) >> 2;
0426 y_ratio = (((mode->vdisplay - 1) << 16) /
0427 (adjusted_mode->crtc_vdisplay - 1)) >> 2;
0428 ivch_write(dvo, VR42, x_ratio);
0429 ivch_write(dvo, VR41, y_ratio);
0430 } else {
0431 vr01 &= ~VR01_PANEL_FIT_ENABLE;
0432 vr40 &= ~VR40_CLOCK_GATING_ENABLE;
0433 }
0434 vr40 &= ~VR40_AUTO_RATIO_ENABLE;
0435
0436 ivch_write(dvo, VR01, vr01);
0437 ivch_write(dvo, VR40, vr40);
0438 }
0439
0440 static void ivch_dump_regs(struct intel_dvo_device *dvo)
0441 {
0442 u16 val;
0443
0444 ivch_read(dvo, VR00, &val);
0445 DRM_DEBUG_KMS("VR00: 0x%04x\n", val);
0446 ivch_read(dvo, VR01, &val);
0447 DRM_DEBUG_KMS("VR01: 0x%04x\n", val);
0448 ivch_read(dvo, VR10, &val);
0449 DRM_DEBUG_KMS("VR10: 0x%04x\n", val);
0450 ivch_read(dvo, VR30, &val);
0451 DRM_DEBUG_KMS("VR30: 0x%04x\n", val);
0452 ivch_read(dvo, VR40, &val);
0453 DRM_DEBUG_KMS("VR40: 0x%04x\n", val);
0454
0455
0456 ivch_read(dvo, VR80, &val);
0457 DRM_DEBUG_KMS("VR80: 0x%04x\n", val);
0458 ivch_read(dvo, VR81, &val);
0459 DRM_DEBUG_KMS("VR81: 0x%04x\n", val);
0460 ivch_read(dvo, VR82, &val);
0461 DRM_DEBUG_KMS("VR82: 0x%04x\n", val);
0462 ivch_read(dvo, VR83, &val);
0463 DRM_DEBUG_KMS("VR83: 0x%04x\n", val);
0464 ivch_read(dvo, VR84, &val);
0465 DRM_DEBUG_KMS("VR84: 0x%04x\n", val);
0466 ivch_read(dvo, VR85, &val);
0467 DRM_DEBUG_KMS("VR85: 0x%04x\n", val);
0468 ivch_read(dvo, VR86, &val);
0469 DRM_DEBUG_KMS("VR86: 0x%04x\n", val);
0470 ivch_read(dvo, VR87, &val);
0471 DRM_DEBUG_KMS("VR87: 0x%04x\n", val);
0472 ivch_read(dvo, VR88, &val);
0473 DRM_DEBUG_KMS("VR88: 0x%04x\n", val);
0474
0475
0476 ivch_read(dvo, VR8E, &val);
0477 DRM_DEBUG_KMS("VR8E: 0x%04x\n", val);
0478
0479
0480 ivch_read(dvo, VR8F, &val);
0481 DRM_DEBUG_KMS("VR8F: 0x%04x\n", val);
0482 }
0483
0484 static void ivch_destroy(struct intel_dvo_device *dvo)
0485 {
0486 struct ivch_priv *priv = dvo->dev_priv;
0487
0488 if (priv) {
0489 kfree(priv);
0490 dvo->dev_priv = NULL;
0491 }
0492 }
0493
0494 const struct intel_dvo_dev_ops ivch_ops = {
0495 .init = ivch_init,
0496 .dpms = ivch_dpms,
0497 .get_hw_state = ivch_get_hw_state,
0498 .mode_valid = ivch_mode_valid,
0499 .mode_set = ivch_mode_set,
0500 .detect = ivch_detect,
0501 .dump_regs = ivch_dump_regs,
0502 .destroy = ivch_destroy,
0503 };