1 /* $NetBSD: dvo_ch7017.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $ */ 2 3 /* 4 * Copyright 2006 Intel Corporation 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Authors: 26 * Eric Anholt <eric (at) anholt.net> 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: dvo_ch7017.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $"); 32 33 #include "intel_display_types.h" 34 #include "intel_dvo_dev.h" 35 36 #define CH7017_TV_DISPLAY_MODE 0x00 37 #define CH7017_FLICKER_FILTER 0x01 38 #define CH7017_VIDEO_BANDWIDTH 0x02 39 #define CH7017_TEXT_ENHANCEMENT 0x03 40 #define CH7017_START_ACTIVE_VIDEO 0x04 41 #define CH7017_HORIZONTAL_POSITION 0x05 42 #define CH7017_VERTICAL_POSITION 0x06 43 #define CH7017_BLACK_LEVEL 0x07 44 #define CH7017_CONTRAST_ENHANCEMENT 0x08 45 #define CH7017_TV_PLL 0x09 46 #define CH7017_TV_PLL_M 0x0a 47 #define CH7017_TV_PLL_N 0x0b 48 #define CH7017_SUB_CARRIER_0 0x0c 49 #define CH7017_CIV_CONTROL 0x10 50 #define CH7017_CIV_0 0x11 51 #define CH7017_CHROMA_BOOST 0x14 52 #define CH7017_CLOCK_MODE 0x1c 53 #define CH7017_INPUT_CLOCK 0x1d 54 #define CH7017_GPIO_CONTROL 0x1e 55 #define CH7017_INPUT_DATA_FORMAT 0x1f 56 #define CH7017_CONNECTION_DETECT 0x20 57 #define CH7017_DAC_CONTROL 0x21 58 #define CH7017_BUFFERED_CLOCK_OUTPUT 0x22 59 #define CH7017_DEFEAT_VSYNC 0x47 60 #define CH7017_TEST_PATTERN 0x48 61 62 #define CH7017_POWER_MANAGEMENT 0x49 63 /** Enables the TV output path. */ 64 #define CH7017_TV_EN (1 << 0) 65 #define CH7017_DAC0_POWER_DOWN (1 << 1) 66 #define CH7017_DAC1_POWER_DOWN (1 << 2) 67 #define CH7017_DAC2_POWER_DOWN (1 << 3) 68 #define CH7017_DAC3_POWER_DOWN (1 << 4) 69 /** Powers down the TV out block, and DAC0-3 */ 70 #define CH7017_TV_POWER_DOWN_EN (1 << 5) 71 72 #define CH7017_VERSION_ID 0x4a 73 74 #define CH7017_DEVICE_ID 0x4b 75 #define CH7017_DEVICE_ID_VALUE 0x1b 76 #define CH7018_DEVICE_ID_VALUE 0x1a 77 #define CH7019_DEVICE_ID_VALUE 0x19 78 79 #define CH7017_XCLK_D2_ADJUST 0x53 80 #define CH7017_UP_SCALER_COEFF_0 0x55 81 #define CH7017_UP_SCALER_COEFF_1 0x56 82 #define CH7017_UP_SCALER_COEFF_2 0x57 83 #define CH7017_UP_SCALER_COEFF_3 0x58 84 #define CH7017_UP_SCALER_COEFF_4 0x59 85 #define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a 86 #define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b 87 #define CH7017_GPIO_INVERT 0x5c 88 #define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d 89 #define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e 90 91 #define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f 92 /**< Low bits of horizontal active pixel input */ 93 94 #define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60 95 /** High bits of horizontal active pixel input */ 96 #define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0) 97 /** High bits of vertical active line output */ 98 #define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3) 99 100 #define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61 101 /**< Low bits of vertical active line output */ 102 103 #define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62 104 /**< Low bits of horizontal active pixel output */ 105 106 #define CH7017_LVDS_POWER_DOWN 0x63 107 /** High bits of horizontal active pixel output */ 108 #define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0) 109 /** Enables the LVDS power down state transition */ 110 #define CH7017_LVDS_POWER_DOWN_EN (1 << 6) 111 /** Enables the LVDS upscaler */ 112 #define CH7017_LVDS_UPSCALER_EN (1 << 7) 113 #define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08 114 115 #define CH7017_LVDS_ENCODING 0x64 116 #define CH7017_LVDS_DITHER_2D (1 << 2) 117 #define CH7017_LVDS_DITHER_DIS (1 << 3) 118 #define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4) 119 #define CH7017_LVDS_24_BIT (1 << 5) 120 121 #define CH7017_LVDS_ENCODING_2 0x65 122 123 #define CH7017_LVDS_PLL_CONTROL 0x66 124 /** Enables the LVDS panel output path */ 125 #define CH7017_LVDS_PANEN (1 << 0) 126 /** Enables the LVDS panel backlight */ 127 #define CH7017_LVDS_BKLEN (1 << 3) 128 129 #define CH7017_POWER_SEQUENCING_T1 0x67 130 #define CH7017_POWER_SEQUENCING_T2 0x68 131 #define CH7017_POWER_SEQUENCING_T3 0x69 132 #define CH7017_POWER_SEQUENCING_T4 0x6a 133 #define CH7017_POWER_SEQUENCING_T5 0x6b 134 #define CH7017_GPIO_DRIVER_TYPE 0x6c 135 #define CH7017_GPIO_DATA 0x6d 136 #define CH7017_GPIO_DIRECTION_CONTROL 0x6e 137 138 #define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71 139 # define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4 140 # define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0 141 # define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80 142 143 #define CH7017_LVDS_PLL_VCO_CONTROL 0x72 144 # define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80 145 # define CH7017_LVDS_PLL_VCO_SHIFT 4 146 # define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0 147 148 #define CH7017_OUTPUTS_ENABLE 0x73 149 # define CH7017_CHARGE_PUMP_LOW 0x0 150 # define CH7017_CHARGE_PUMP_HIGH 0x3 151 # define CH7017_LVDS_CHANNEL_A (1 << 3) 152 # define CH7017_LVDS_CHANNEL_B (1 << 4) 153 # define CH7017_TV_DAC_A (1 << 5) 154 # define CH7017_TV_DAC_B (1 << 6) 155 # define CH7017_DDC_SELECT_DC2 (1 << 7) 156 157 #define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74 158 #define CH7017_LVDS_PLL_EMI_REDUCTION 0x75 159 #define CH7017_LVDS_POWER_DOWN_FLICKER 0x76 160 161 #define CH7017_LVDS_CONTROL_2 0x78 162 # define CH7017_LOOP_FILTER_SHIFT 5 163 # define CH7017_PHASE_DETECTOR_SHIFT 0 164 165 #define CH7017_BANG_LIMIT_CONTROL 0x7f 166 167 struct ch7017_priv { 168 u8 dummy; 169 }; 170 171 static void ch7017_dump_regs(struct intel_dvo_device *dvo); 172 static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable); 173 174 static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val) 175 { 176 struct i2c_msg msgs[] = { 177 { 178 .addr = dvo->slave_addr, 179 .flags = 0, 180 .len = 1, 181 .buf = &addr, 182 }, 183 { 184 .addr = dvo->slave_addr, 185 .flags = I2C_M_RD, 186 .len = 1, 187 .buf = val, 188 } 189 }; 190 return i2c_transfer(dvo->i2c_bus, msgs, 2) == 2; 191 } 192 193 static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val) 194 { 195 u8 buf[2] = { addr, val }; 196 struct i2c_msg msg = { 197 .addr = dvo->slave_addr, 198 .flags = 0, 199 .len = 2, 200 .buf = buf, 201 }; 202 return i2c_transfer(dvo->i2c_bus, &msg, 1) == 1; 203 } 204 205 /** Probes for a CH7017 on the given bus and slave address. */ 206 static bool ch7017_init(struct intel_dvo_device *dvo, 207 struct i2c_adapter *adapter) 208 { 209 struct ch7017_priv *priv; 210 const char *str; 211 u8 val; 212 213 priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); 214 if (priv == NULL) 215 return false; 216 217 dvo->i2c_bus = adapter; 218 dvo->dev_priv = priv; 219 220 if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val)) 221 goto fail; 222 223 switch (val) { 224 case CH7017_DEVICE_ID_VALUE: 225 str = "ch7017"; 226 break; 227 case CH7018_DEVICE_ID_VALUE: 228 str = "ch7018"; 229 break; 230 case CH7019_DEVICE_ID_VALUE: 231 str = "ch7019"; 232 break; 233 default: 234 DRM_DEBUG_KMS("ch701x not detected, got %d: from %s " 235 "slave %d.\n", 236 val, adapter->name, dvo->slave_addr); 237 goto fail; 238 } 239 240 DRM_DEBUG_KMS("%s detected on %s, addr %d\n", 241 str, adapter->name, dvo->slave_addr); 242 return true; 243 244 fail: 245 kfree(priv); 246 return false; 247 } 248 249 static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo) 250 { 251 return connector_status_connected; 252 } 253 254 static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo, 255 struct drm_display_mode *mode) 256 { 257 if (mode->clock > 160000) 258 return MODE_CLOCK_HIGH; 259 260 return MODE_OK; 261 } 262 263 static void ch7017_mode_set(struct intel_dvo_device *dvo, 264 const struct drm_display_mode *mode, 265 const struct drm_display_mode *adjusted_mode) 266 { 267 u8 lvds_pll_feedback_div, lvds_pll_vco_control; 268 u8 outputs_enable, lvds_control_2, lvds_power_down; 269 u8 horizontal_active_pixel_input; 270 u8 horizontal_active_pixel_output, vertical_active_line_output; 271 u8 active_input_line_output; 272 273 DRM_DEBUG_KMS("Registers before mode setting\n"); 274 ch7017_dump_regs(dvo); 275 276 /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/ 277 if (mode->clock < 100000) { 278 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW; 279 lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | 280 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | 281 (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); 282 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 283 (2 << CH7017_LVDS_PLL_VCO_SHIFT) | 284 (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 285 lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) | 286 (0 << CH7017_PHASE_DETECTOR_SHIFT); 287 } else { 288 outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH; 289 lvds_pll_feedback_div = 290 CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | 291 (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | 292 (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); 293 lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) | 294 (0 << CH7017_PHASE_DETECTOR_SHIFT); 295 if (1) { /* XXX: dual channel panel detection. Assume yes for now. */ 296 outputs_enable |= CH7017_LVDS_CHANNEL_B; 297 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 298 (2 << CH7017_LVDS_PLL_VCO_SHIFT) | 299 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 300 } else { 301 lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | 302 (1 << CH7017_LVDS_PLL_VCO_SHIFT) | 303 (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); 304 } 305 } 306 307 horizontal_active_pixel_input = mode->hdisplay & 0x00ff; 308 309 vertical_active_line_output = mode->vdisplay & 0x00ff; 310 horizontal_active_pixel_output = mode->hdisplay & 0x00ff; 311 312 active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) | 313 (((mode->vdisplay & 0x0700) >> 8) << 3); 314 315 lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | 316 (mode->hdisplay & 0x0700) >> 8; 317 318 ch7017_dpms(dvo, false); 319 ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, 320 horizontal_active_pixel_input); 321 ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, 322 horizontal_active_pixel_output); 323 ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, 324 vertical_active_line_output); 325 ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, 326 active_input_line_output); 327 ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control); 328 ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div); 329 ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2); 330 ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable); 331 332 /* Turn the LVDS back on with new settings. */ 333 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down); 334 335 DRM_DEBUG_KMS("Registers after mode setting\n"); 336 ch7017_dump_regs(dvo); 337 } 338 339 /* set the CH7017 power state */ 340 static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable) 341 { 342 u8 val; 343 344 ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); 345 346 /* Turn off TV/VGA, and never turn it on since we don't support it. */ 347 ch7017_write(dvo, CH7017_POWER_MANAGEMENT, 348 CH7017_DAC0_POWER_DOWN | 349 CH7017_DAC1_POWER_DOWN | 350 CH7017_DAC2_POWER_DOWN | 351 CH7017_DAC3_POWER_DOWN | 352 CH7017_TV_POWER_DOWN_EN); 353 354 if (enable) { 355 /* Turn on the LVDS */ 356 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, 357 val & ~CH7017_LVDS_POWER_DOWN_EN); 358 } else { 359 /* Turn off the LVDS */ 360 ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, 361 val | CH7017_LVDS_POWER_DOWN_EN); 362 } 363 364 /* XXX: Should actually wait for update power status somehow */ 365 msleep(20); 366 } 367 368 static bool ch7017_get_hw_state(struct intel_dvo_device *dvo) 369 { 370 u8 val; 371 372 ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); 373 374 if (val & CH7017_LVDS_POWER_DOWN_EN) 375 return false; 376 else 377 return true; 378 } 379 380 static void ch7017_dump_regs(struct intel_dvo_device *dvo) 381 { 382 u8 val; 383 384 #define DUMP(reg) \ 385 do { \ 386 ch7017_read(dvo, reg, &val); \ 387 DRM_DEBUG_KMS(#reg ": %02x\n", val); \ 388 } while (0) 389 390 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT); 391 DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT); 392 DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT); 393 DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT); 394 DUMP(CH7017_LVDS_PLL_VCO_CONTROL); 395 DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV); 396 DUMP(CH7017_LVDS_CONTROL_2); 397 DUMP(CH7017_OUTPUTS_ENABLE); 398 DUMP(CH7017_LVDS_POWER_DOWN); 399 } 400 401 static void ch7017_destroy(struct intel_dvo_device *dvo) 402 { 403 struct ch7017_priv *priv = dvo->dev_priv; 404 405 if (priv) { 406 kfree(priv); 407 dvo->dev_priv = NULL; 408 } 409 } 410 411 const struct intel_dvo_dev_ops ch7017_ops = { 412 .init = ch7017_init, 413 .detect = ch7017_detect, 414 .mode_valid = ch7017_mode_valid, 415 .mode_set = ch7017_mode_set, 416 .dpms = ch7017_dpms, 417 .get_hw_state = ch7017_get_hw_state, 418 .dump_regs = ch7017_dump_regs, 419 .destroy = ch7017_destroy, 420 }; 421