diff options
| author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2012-11-29 03:34:41 -0500 |
|---|---|---|
| committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2012-11-29 03:34:41 -0500 |
| commit | 473af20f705b690342af1bfb36462c0c5190d289 (patch) | |
| tree | 1f48932284bf30bb9d83bbedba8caa84aca51341 | |
| parent | fa1f94979883aeef231fa023e5b9be971d459952 (diff) | |
| parent | 3fcb6eb4063ab4eef05601c266afa2af667c8e1f (diff) | |
Merge branch 'exynos-dp-next' of git://github.com/jingoo/linux into for-linus
Exynos DP changes for the 3.8 merge window.
- Device Tree support for Samsung Exynos DP
- SW Link training is cleaned up.
- HPD interrupt is supported.
* 'exynos-dp-next' of git://github.com/jingoo/linux:
video: exynos_dp: remove redundant parameters
video: exynos_dp: Fix incorrect setting for INT_CTL
video: exynos_dp: Reset and initialize DP before requesting irq
video: exynos_dp: Enable hotplug interrupts
video: exynos_dp: Move hotplug into a workqueue
video: exynos_dp: Remove sink control to D0
video: exynos_dp: Fix bug when checking dp->irq
video: exynos_dp: Improve EDID error handling
video: exynos_dp: Get pll lock before pattern set
video: exynos_dp: Clean up SW link training
video: exynos_dp: Check DPCD return codes
video: exynos_dp: device tree documentation
video: exynos_dp: Add device tree support to DP driver
| -rw-r--r-- | Documentation/devicetree/bindings/video/exynos_dp.txt | 80 | ||||
| -rw-r--r-- | drivers/video/exynos/exynos_dp_core.c | 697 | ||||
| -rw-r--r-- | drivers/video/exynos/exynos_dp_core.h | 21 | ||||
| -rw-r--r-- | drivers/video/exynos/exynos_dp_reg.c | 77 | ||||
| -rw-r--r-- | drivers/video/exynos/exynos_dp_reg.h | 3 |
5 files changed, 583 insertions, 295 deletions
diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt new file mode 100644 index 000000000000..c60da67a5d76 --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos_dp.txt | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | The Exynos display port interface should be configured based on | ||
| 2 | the type of panel connected to it. | ||
| 3 | |||
| 4 | We use two nodes: | ||
| 5 | -dp-controller node | ||
| 6 | -dptx-phy node(defined inside dp-controller node) | ||
| 7 | |||
| 8 | For the DP-PHY initialization, we use the dptx-phy node. | ||
| 9 | Required properties for dptx-phy: | ||
| 10 | -reg: | ||
| 11 | Base address of DP PHY register. | ||
| 12 | -samsung,enable-mask: | ||
| 13 | The bit-mask used to enable/disable DP PHY. | ||
| 14 | |||
| 15 | For the Panel initialization, we read data from dp-controller node. | ||
| 16 | Required properties for dp-controller: | ||
| 17 | -compatible: | ||
| 18 | should be "samsung,exynos5-dp". | ||
| 19 | -reg: | ||
| 20 | physical base address of the controller and length | ||
| 21 | of memory mapped region. | ||
| 22 | -interrupts: | ||
| 23 | interrupt combiner values. | ||
| 24 | -interrupt-parent: | ||
| 25 | phandle to Interrupt combiner node. | ||
| 26 | -samsung,color-space: | ||
| 27 | input video data format. | ||
| 28 | COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2 | ||
| 29 | -samsung,dynamic-range: | ||
| 30 | dynamic range for input video data. | ||
| 31 | VESA = 0, CEA = 1 | ||
| 32 | -samsung,ycbcr-coeff: | ||
| 33 | YCbCr co-efficients for input video. | ||
| 34 | COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1 | ||
| 35 | -samsung,color-depth: | ||
| 36 | number of bits per colour component. | ||
| 37 | COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3 | ||
| 38 | -samsung,link-rate: | ||
| 39 | link rate supported by the panel. | ||
| 40 | LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A | ||
| 41 | -samsung,lane-count: | ||
| 42 | number of lanes supported by the panel. | ||
| 43 | LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4 | ||
| 44 | |||
| 45 | Optional properties for dp-controller: | ||
| 46 | -interlaced: | ||
| 47 | interlace scan mode. | ||
| 48 | Progressive if defined, Interlaced if not defined | ||
| 49 | -vsync-active-high: | ||
| 50 | VSYNC polarity configuration. | ||
| 51 | High if defined, Low if not defined | ||
| 52 | -hsync-active-high: | ||
| 53 | HSYNC polarity configuration. | ||
| 54 | High if defined, Low if not defined | ||
| 55 | |||
| 56 | Example: | ||
| 57 | |||
| 58 | SOC specific portion: | ||
| 59 | dp-controller { | ||
| 60 | compatible = "samsung,exynos5-dp"; | ||
| 61 | reg = <0x145b0000 0x10000>; | ||
| 62 | interrupts = <10 3>; | ||
| 63 | interrupt-parent = <&combiner>; | ||
| 64 | |||
| 65 | dptx-phy { | ||
| 66 | reg = <0x10040720>; | ||
| 67 | samsung,enable-mask = <1>; | ||
| 68 | }; | ||
| 69 | |||
| 70 | }; | ||
| 71 | |||
| 72 | Board Specific portion: | ||
| 73 | dp-controller { | ||
| 74 | samsung,color-space = <0>; | ||
| 75 | samsung,dynamic-range = <0>; | ||
| 76 | samsung,ycbcr-coeff = <0>; | ||
| 77 | samsung,color-depth = <1>; | ||
| 78 | samsung,link-rate = <0x0a>; | ||
| 79 | samsung,lane-count = <4>; | ||
| 80 | }; | ||
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index d55470e75412..28fd686c6b81 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/io.h> | 18 | #include <linux/io.h> |
| 19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
| 20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
| 21 | #include <linux/of.h> | ||
| 21 | 22 | ||
| 22 | #include <video/exynos_dp.h> | 23 | #include <video/exynos_dp.h> |
| 23 | 24 | ||
| @@ -48,10 +49,6 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp) | |||
| 48 | { | 49 | { |
| 49 | int timeout_loop = 0; | 50 | int timeout_loop = 0; |
| 50 | 51 | ||
| 51 | exynos_dp_init_hpd(dp); | ||
| 52 | |||
| 53 | usleep_range(200, 210); | ||
| 54 | |||
| 55 | while (exynos_dp_get_plug_in_status(dp) != 0) { | 52 | while (exynos_dp_get_plug_in_status(dp) != 0) { |
| 56 | timeout_loop++; | 53 | timeout_loop++; |
| 57 | if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { | 54 | if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { |
| @@ -90,9 +87,11 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp) | |||
| 90 | */ | 87 | */ |
| 91 | 88 | ||
| 92 | /* Read Extension Flag, Number of 128-byte EDID extension blocks */ | 89 | /* Read Extension Flag, Number of 128-byte EDID extension blocks */ |
| 93 | exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, | 90 | retval = exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, |
| 94 | EDID_EXTENSION_FLAG, | 91 | EDID_EXTENSION_FLAG, |
| 95 | &extend_block); | 92 | &extend_block); |
| 93 | if (retval) | ||
| 94 | return retval; | ||
| 96 | 95 | ||
| 97 | if (extend_block > 0) { | 96 | if (extend_block > 0) { |
| 98 | dev_dbg(dp->dev, "EDID data includes a single extension!\n"); | 97 | dev_dbg(dp->dev, "EDID data includes a single extension!\n"); |
| @@ -181,14 +180,15 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp) | |||
| 181 | int retval; | 180 | int retval; |
| 182 | 181 | ||
| 183 | /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ | 182 | /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */ |
| 184 | exynos_dp_read_bytes_from_dpcd(dp, | 183 | retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV, |
| 185 | DPCD_ADDR_DPCD_REV, | 184 | 12, buf); |
| 186 | 12, buf); | 185 | if (retval) |
| 186 | return retval; | ||
| 187 | 187 | ||
| 188 | /* Read EDID */ | 188 | /* Read EDID */ |
| 189 | for (i = 0; i < 3; i++) { | 189 | for (i = 0; i < 3; i++) { |
| 190 | retval = exynos_dp_read_edid(dp); | 190 | retval = exynos_dp_read_edid(dp); |
| 191 | if (retval == 0) | 191 | if (!retval) |
| 192 | break; | 192 | break; |
| 193 | } | 193 | } |
| 194 | 194 | ||
| @@ -261,11 +261,10 @@ static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp, | |||
| 261 | } | 261 | } |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | static void exynos_dp_link_start(struct exynos_dp_device *dp) | 264 | static int exynos_dp_link_start(struct exynos_dp_device *dp) |
| 265 | { | 265 | { |
| 266 | u8 buf[4]; | 266 | u8 buf[4]; |
| 267 | int lane; | 267 | int lane, lane_count, pll_tries, retval; |
| 268 | int lane_count; | ||
| 269 | 268 | ||
| 270 | lane_count = dp->link_train.lane_count; | 269 | lane_count = dp->link_train.lane_count; |
| 271 | 270 | ||
| @@ -275,10 +274,6 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp) | |||
| 275 | for (lane = 0; lane < lane_count; lane++) | 274 | for (lane = 0; lane < lane_count; lane++) |
| 276 | dp->link_train.cr_loop[lane] = 0; | 275 | dp->link_train.cr_loop[lane] = 0; |
| 277 | 276 | ||
| 278 | /* Set sink to D0 (Sink Not Ready) mode. */ | ||
| 279 | exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, | ||
| 280 | DPCD_SET_POWER_STATE_D0); | ||
| 281 | |||
| 282 | /* Set link rate and count as you want to establish*/ | 277 | /* Set link rate and count as you want to establish*/ |
| 283 | exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); | 278 | exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate); |
| 284 | exynos_dp_set_lane_count(dp, dp->link_train.lane_count); | 279 | exynos_dp_set_lane_count(dp, dp->link_train.lane_count); |
| @@ -286,29 +281,46 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp) | |||
| 286 | /* Setup RX configuration */ | 281 | /* Setup RX configuration */ |
| 287 | buf[0] = dp->link_train.link_rate; | 282 | buf[0] = dp->link_train.link_rate; |
| 288 | buf[1] = dp->link_train.lane_count; | 283 | buf[1] = dp->link_train.lane_count; |
| 289 | exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, | 284 | retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET, |
| 290 | 2, buf); | 285 | 2, buf); |
| 286 | if (retval) | ||
| 287 | return retval; | ||
| 291 | 288 | ||
| 292 | /* Set TX pre-emphasis to minimum */ | 289 | /* Set TX pre-emphasis to minimum */ |
| 293 | for (lane = 0; lane < lane_count; lane++) | 290 | for (lane = 0; lane < lane_count; lane++) |
| 294 | exynos_dp_set_lane_lane_pre_emphasis(dp, | 291 | exynos_dp_set_lane_lane_pre_emphasis(dp, |
| 295 | PRE_EMPHASIS_LEVEL_0, lane); | 292 | PRE_EMPHASIS_LEVEL_0, lane); |
| 296 | 293 | ||
| 294 | /* Wait for PLL lock */ | ||
| 295 | pll_tries = 0; | ||
| 296 | while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { | ||
| 297 | if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { | ||
| 298 | dev_err(dp->dev, "Wait for PLL lock timed out\n"); | ||
| 299 | return -ETIMEDOUT; | ||
| 300 | } | ||
| 301 | |||
| 302 | pll_tries++; | ||
| 303 | usleep_range(90, 120); | ||
| 304 | } | ||
| 305 | |||
| 297 | /* Set training pattern 1 */ | 306 | /* Set training pattern 1 */ |
| 298 | exynos_dp_set_training_pattern(dp, TRAINING_PTN1); | 307 | exynos_dp_set_training_pattern(dp, TRAINING_PTN1); |
| 299 | 308 | ||
| 300 | /* Set RX training pattern */ | 309 | /* Set RX training pattern */ |
| 301 | exynos_dp_write_byte_to_dpcd(dp, | 310 | retval = exynos_dp_write_byte_to_dpcd(dp, |
| 302 | DPCD_ADDR_TRAINING_PATTERN_SET, | 311 | DPCD_ADDR_TRAINING_PATTERN_SET, |
| 303 | DPCD_SCRAMBLING_DISABLED | | 312 | DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1); |
| 304 | DPCD_TRAINING_PATTERN_1); | 313 | if (retval) |
| 314 | return retval; | ||
| 305 | 315 | ||
| 306 | for (lane = 0; lane < lane_count; lane++) | 316 | for (lane = 0; lane < lane_count; lane++) |
| 307 | buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | | 317 | buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | |
| 308 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; | 318 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; |
| 309 | exynos_dp_write_bytes_to_dpcd(dp, | 319 | |
| 310 | DPCD_ADDR_TRAINING_LANE0_SET, | 320 | retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, |
| 311 | lane_count, buf); | 321 | lane_count, buf); |
| 322 | |||
| 323 | return retval; | ||
| 312 | } | 324 | } |
| 313 | 325 | ||
| 314 | static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane) | 326 | static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane) |
| @@ -332,18 +344,17 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count) | |||
| 332 | return 0; | 344 | return 0; |
| 333 | } | 345 | } |
| 334 | 346 | ||
| 335 | static int exynos_dp_channel_eq_ok(u8 link_align[3], int lane_count) | 347 | static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align, |
| 348 | int lane_count) | ||
| 336 | { | 349 | { |
| 337 | int lane; | 350 | int lane; |
| 338 | u8 lane_align; | ||
| 339 | u8 lane_status; | 351 | u8 lane_status; |
| 340 | 352 | ||
| 341 | lane_align = link_align[2]; | 353 | if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0) |
| 342 | if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0) | ||
| 343 | return -EINVAL; | 354 | return -EINVAL; |
| 344 | 355 | ||
| 345 | for (lane = 0; lane < lane_count; lane++) { | 356 | for (lane = 0; lane < lane_count; lane++) { |
| 346 | lane_status = exynos_dp_get_lane_status(link_align, lane); | 357 | lane_status = exynos_dp_get_lane_status(link_status, lane); |
| 347 | lane_status &= DPCD_CHANNEL_EQ_BITS; | 358 | lane_status &= DPCD_CHANNEL_EQ_BITS; |
| 348 | if (lane_status != DPCD_CHANNEL_EQ_BITS) | 359 | if (lane_status != DPCD_CHANNEL_EQ_BITS) |
| 349 | return -EINVAL; | 360 | return -EINVAL; |
| @@ -427,60 +438,60 @@ static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) | |||
| 427 | dp->link_train.lt_state = FAILED; | 438 | dp->link_train.lt_state = FAILED; |
| 428 | } | 439 | } |
| 429 | 440 | ||
| 430 | static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | 441 | static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp, |
| 442 | u8 adjust_request[2]) | ||
| 431 | { | 443 | { |
| 432 | u8 link_status[2]; | 444 | int lane, lane_count; |
| 433 | int lane; | 445 | u8 voltage_swing, pre_emphasis, training_lane; |
| 434 | int lane_count; | ||
| 435 | 446 | ||
| 436 | u8 adjust_request[2]; | 447 | lane_count = dp->link_train.lane_count; |
| 437 | u8 voltage_swing; | 448 | for (lane = 0; lane < lane_count; lane++) { |
| 438 | u8 pre_emphasis; | 449 | voltage_swing = exynos_dp_get_adjust_request_voltage( |
| 439 | u8 training_lane; | 450 | adjust_request, lane); |
| 451 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | ||
| 452 | adjust_request, lane); | ||
| 453 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
| 454 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
| 455 | |||
| 456 | if (voltage_swing == VOLTAGE_LEVEL_3) | ||
| 457 | training_lane |= DPCD_MAX_SWING_REACHED; | ||
| 458 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | ||
| 459 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | ||
| 460 | |||
| 461 | dp->link_train.training_lane[lane] = training_lane; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | ||
| 466 | { | ||
| 467 | int lane, lane_count, retval; | ||
| 468 | u8 voltage_swing, pre_emphasis, training_lane; | ||
| 469 | u8 link_status[2], adjust_request[2]; | ||
| 440 | 470 | ||
| 441 | usleep_range(100, 101); | 471 | usleep_range(100, 101); |
| 442 | 472 | ||
| 443 | lane_count = dp->link_train.lane_count; | 473 | lane_count = dp->link_train.lane_count; |
| 444 | 474 | ||
| 445 | exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | 475 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
| 446 | 2, link_status); | 476 | DPCD_ADDR_LANE0_1_STATUS, 2, link_status); |
| 477 | if (retval) | ||
| 478 | return retval; | ||
| 479 | |||
| 480 | retval = exynos_dp_read_bytes_from_dpcd(dp, | ||
| 481 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); | ||
| 482 | if (retval) | ||
| 483 | return retval; | ||
| 447 | 484 | ||
| 448 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { | 485 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { |
| 449 | /* set training pattern 2 for EQ */ | 486 | /* set training pattern 2 for EQ */ |
| 450 | exynos_dp_set_training_pattern(dp, TRAINING_PTN2); | 487 | exynos_dp_set_training_pattern(dp, TRAINING_PTN2); |
| 451 | 488 | ||
| 452 | for (lane = 0; lane < lane_count; lane++) { | 489 | retval = exynos_dp_write_byte_to_dpcd(dp, |
| 453 | exynos_dp_read_bytes_from_dpcd(dp, | 490 | DPCD_ADDR_TRAINING_PATTERN_SET, |
| 454 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | 491 | DPCD_SCRAMBLING_DISABLED | |
| 455 | 2, adjust_request); | 492 | DPCD_TRAINING_PATTERN_2); |
| 456 | voltage_swing = exynos_dp_get_adjust_request_voltage( | 493 | if (retval) |
| 457 | adjust_request, lane); | 494 | return retval; |
| 458 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | ||
| 459 | adjust_request, lane); | ||
| 460 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
| 461 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
| 462 | |||
| 463 | if (voltage_swing == VOLTAGE_LEVEL_3) | ||
| 464 | training_lane |= DPCD_MAX_SWING_REACHED; | ||
| 465 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | ||
| 466 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | ||
| 467 | |||
| 468 | dp->link_train.training_lane[lane] = training_lane; | ||
| 469 | |||
| 470 | exynos_dp_set_lane_link_training(dp, | ||
| 471 | dp->link_train.training_lane[lane], | ||
| 472 | lane); | ||
| 473 | } | ||
| 474 | |||
| 475 | exynos_dp_write_byte_to_dpcd(dp, | ||
| 476 | DPCD_ADDR_TRAINING_PATTERN_SET, | ||
| 477 | DPCD_SCRAMBLING_DISABLED | | ||
| 478 | DPCD_TRAINING_PATTERN_2); | ||
| 479 | |||
| 480 | exynos_dp_write_bytes_to_dpcd(dp, | ||
| 481 | DPCD_ADDR_TRAINING_LANE0_SET, | ||
| 482 | lane_count, | ||
| 483 | dp->link_train.training_lane); | ||
| 484 | 495 | ||
| 485 | dev_info(dp->dev, "Link Training Clock Recovery success\n"); | 496 | dev_info(dp->dev, "Link Training Clock Recovery success\n"); |
| 486 | dp->link_train.lt_state = EQUALIZER_TRAINING; | 497 | dp->link_train.lt_state = EQUALIZER_TRAINING; |
| @@ -488,152 +499,116 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | |||
| 488 | for (lane = 0; lane < lane_count; lane++) { | 499 | for (lane = 0; lane < lane_count; lane++) { |
| 489 | training_lane = exynos_dp_get_lane_link_training( | 500 | training_lane = exynos_dp_get_lane_link_training( |
| 490 | dp, lane); | 501 | dp, lane); |
| 491 | exynos_dp_read_bytes_from_dpcd(dp, | ||
| 492 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | ||
| 493 | 2, adjust_request); | ||
| 494 | voltage_swing = exynos_dp_get_adjust_request_voltage( | 502 | voltage_swing = exynos_dp_get_adjust_request_voltage( |
| 495 | adjust_request, lane); | 503 | adjust_request, lane); |
| 496 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | 504 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( |
| 497 | adjust_request, lane); | 505 | adjust_request, lane); |
| 498 | 506 | ||
| 499 | if (voltage_swing == VOLTAGE_LEVEL_3 || | 507 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == |
| 500 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { | 508 | voltage_swing && |
| 501 | dev_err(dp->dev, "voltage or pre emphasis reached max level\n"); | 509 | DPCD_PRE_EMPHASIS_GET(training_lane) == |
| 502 | goto reduce_link_rate; | 510 | pre_emphasis) |
| 503 | } | ||
| 504 | |||
| 505 | if ((DPCD_VOLTAGE_SWING_GET(training_lane) == | ||
| 506 | voltage_swing) && | ||
| 507 | (DPCD_PRE_EMPHASIS_GET(training_lane) == | ||
| 508 | pre_emphasis)) { | ||
| 509 | dp->link_train.cr_loop[lane]++; | 511 | dp->link_train.cr_loop[lane]++; |
| 510 | if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP) { | ||
| 511 | dev_err(dp->dev, "CR Max loop\n"); | ||
| 512 | goto reduce_link_rate; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
| 517 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
| 518 | 512 | ||
| 519 | if (voltage_swing == VOLTAGE_LEVEL_3) | 513 | if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || |
| 520 | training_lane |= DPCD_MAX_SWING_REACHED; | 514 | voltage_swing == VOLTAGE_LEVEL_3 || |
| 521 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | 515 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { |
| 522 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | 516 | dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", |
| 517 | dp->link_train.cr_loop[lane], | ||
| 518 | voltage_swing, pre_emphasis); | ||
| 519 | exynos_dp_reduce_link_rate(dp); | ||
| 520 | return -EIO; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | } | ||
| 523 | 524 | ||
| 524 | dp->link_train.training_lane[lane] = training_lane; | 525 | exynos_dp_get_adjust_training_lane(dp, adjust_request); |
| 525 | 526 | ||
| 526 | exynos_dp_set_lane_link_training(dp, | 527 | for (lane = 0; lane < lane_count; lane++) |
| 527 | dp->link_train.training_lane[lane], lane); | 528 | exynos_dp_set_lane_link_training(dp, |
| 528 | } | 529 | dp->link_train.training_lane[lane], lane); |
| 529 | 530 | ||
| 530 | exynos_dp_write_bytes_to_dpcd(dp, | 531 | retval = exynos_dp_write_bytes_to_dpcd(dp, |
| 531 | DPCD_ADDR_TRAINING_LANE0_SET, | 532 | DPCD_ADDR_TRAINING_LANE0_SET, lane_count, |
| 532 | lane_count, | ||
| 533 | dp->link_train.training_lane); | 533 | dp->link_train.training_lane); |
| 534 | } | 534 | if (retval) |
| 535 | 535 | return retval; | |
| 536 | return 0; | ||
| 537 | 536 | ||
| 538 | reduce_link_rate: | 537 | return retval; |
| 539 | exynos_dp_reduce_link_rate(dp); | ||
| 540 | return -EIO; | ||
| 541 | } | 538 | } |
| 542 | 539 | ||
| 543 | static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) | 540 | static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) |
| 544 | { | 541 | { |
| 545 | u8 link_status[2]; | 542 | int lane, lane_count, retval; |
| 546 | u8 link_align[3]; | ||
| 547 | int lane; | ||
| 548 | int lane_count; | ||
| 549 | u32 reg; | 543 | u32 reg; |
| 550 | 544 | u8 link_align, link_status[2], adjust_request[2]; | |
| 551 | u8 adjust_request[2]; | ||
| 552 | u8 voltage_swing; | ||
| 553 | u8 pre_emphasis; | ||
| 554 | u8 training_lane; | ||
| 555 | 545 | ||
| 556 | usleep_range(400, 401); | 546 | usleep_range(400, 401); |
| 557 | 547 | ||
| 558 | lane_count = dp->link_train.lane_count; | 548 | lane_count = dp->link_train.lane_count; |
| 559 | 549 | ||
| 560 | exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | 550 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
| 561 | 2, link_status); | 551 | DPCD_ADDR_LANE0_1_STATUS, 2, link_status); |
| 552 | if (retval) | ||
| 553 | return retval; | ||
| 562 | 554 | ||
| 563 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { | 555 | if (exynos_dp_clock_recovery_ok(link_status, lane_count)) { |
| 564 | link_align[0] = link_status[0]; | 556 | exynos_dp_reduce_link_rate(dp); |
| 565 | link_align[1] = link_status[1]; | 557 | return -EIO; |
| 558 | } | ||
| 566 | 559 | ||
| 567 | exynos_dp_read_byte_from_dpcd(dp, | 560 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
| 568 | DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, | 561 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); |
| 569 | &link_align[2]); | 562 | if (retval) |
| 563 | return retval; | ||
| 570 | 564 | ||
| 571 | for (lane = 0; lane < lane_count; lane++) { | 565 | retval = exynos_dp_read_byte_from_dpcd(dp, |
| 572 | exynos_dp_read_bytes_from_dpcd(dp, | 566 | DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align); |
| 573 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | 567 | if (retval) |
| 574 | 2, adjust_request); | 568 | return retval; |
| 575 | voltage_swing = exynos_dp_get_adjust_request_voltage( | ||
| 576 | adjust_request, lane); | ||
| 577 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | ||
| 578 | adjust_request, lane); | ||
| 579 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
| 580 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
| 581 | 569 | ||
| 582 | if (voltage_swing == VOLTAGE_LEVEL_3) | 570 | exynos_dp_get_adjust_training_lane(dp, adjust_request); |
| 583 | training_lane |= DPCD_MAX_SWING_REACHED; | ||
| 584 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | ||
| 585 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | ||
| 586 | 571 | ||
| 587 | dp->link_train.training_lane[lane] = training_lane; | 572 | if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) { |
| 588 | } | 573 | /* traing pattern Set to Normal */ |
| 574 | exynos_dp_training_pattern_dis(dp); | ||
| 589 | 575 | ||
| 590 | if (exynos_dp_channel_eq_ok(link_align, lane_count) == 0) { | 576 | dev_info(dp->dev, "Link Training success!\n"); |
| 591 | /* traing pattern Set to Normal */ | ||
| 592 | exynos_dp_training_pattern_dis(dp); | ||
| 593 | 577 | ||
| 594 | dev_info(dp->dev, "Link Training success!\n"); | 578 | exynos_dp_get_link_bandwidth(dp, ®); |
| 595 | 579 | dp->link_train.link_rate = reg; | |
| 596 | exynos_dp_get_link_bandwidth(dp, ®); | 580 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", |
| 597 | dp->link_train.link_rate = reg; | 581 | dp->link_train.link_rate); |
| 598 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", | ||
| 599 | dp->link_train.link_rate); | ||
| 600 | 582 | ||
| 601 | exynos_dp_get_lane_count(dp, ®); | 583 | exynos_dp_get_lane_count(dp, ®); |
| 602 | dp->link_train.lane_count = reg; | 584 | dp->link_train.lane_count = reg; |
| 603 | dev_dbg(dp->dev, "final lane count = %.2x\n", | 585 | dev_dbg(dp->dev, "final lane count = %.2x\n", |
| 604 | dp->link_train.lane_count); | 586 | dp->link_train.lane_count); |
| 605 | 587 | ||
| 606 | /* set enhanced mode if available */ | 588 | /* set enhanced mode if available */ |
| 607 | exynos_dp_set_enhanced_mode(dp); | 589 | exynos_dp_set_enhanced_mode(dp); |
| 608 | dp->link_train.lt_state = FINISHED; | 590 | dp->link_train.lt_state = FINISHED; |
| 609 | } else { | ||
| 610 | /* not all locked */ | ||
| 611 | dp->link_train.eq_loop++; | ||
| 612 | 591 | ||
| 613 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { | 592 | return 0; |
| 614 | dev_err(dp->dev, "EQ Max loop\n"); | 593 | } |
| 615 | goto reduce_link_rate; | ||
| 616 | } | ||
| 617 | 594 | ||
| 618 | for (lane = 0; lane < lane_count; lane++) | 595 | /* not all locked */ |
| 619 | exynos_dp_set_lane_link_training(dp, | 596 | dp->link_train.eq_loop++; |
| 620 | dp->link_train.training_lane[lane], | ||
| 621 | lane); | ||
| 622 | 597 | ||
| 623 | exynos_dp_write_bytes_to_dpcd(dp, | 598 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { |
| 624 | DPCD_ADDR_TRAINING_LANE0_SET, | 599 | dev_err(dp->dev, "EQ Max loop\n"); |
| 625 | lane_count, | 600 | exynos_dp_reduce_link_rate(dp); |
| 626 | dp->link_train.training_lane); | 601 | return -EIO; |
| 627 | } | ||
| 628 | } else { | ||
| 629 | goto reduce_link_rate; | ||
| 630 | } | 602 | } |
| 631 | 603 | ||
| 632 | return 0; | 604 | for (lane = 0; lane < lane_count; lane++) |
| 605 | exynos_dp_set_lane_link_training(dp, | ||
| 606 | dp->link_train.training_lane[lane], lane); | ||
| 607 | |||
| 608 | retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, | ||
| 609 | lane_count, dp->link_train.training_lane); | ||
| 633 | 610 | ||
| 634 | reduce_link_rate: | 611 | return retval; |
| 635 | exynos_dp_reduce_link_rate(dp); | ||
| 636 | return -EIO; | ||
| 637 | } | 612 | } |
| 638 | 613 | ||
| 639 | static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, | 614 | static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, |
| @@ -701,16 +676,17 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp, | |||
| 701 | 676 | ||
| 702 | static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) | 677 | static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) |
| 703 | { | 678 | { |
| 704 | int retval = 0; | 679 | int retval = 0, training_finished = 0; |
| 705 | int training_finished = 0; | ||
| 706 | 680 | ||
| 707 | dp->link_train.lt_state = START; | 681 | dp->link_train.lt_state = START; |
| 708 | 682 | ||
| 709 | /* Process here */ | 683 | /* Process here */ |
| 710 | while (!training_finished) { | 684 | while (!retval && !training_finished) { |
| 711 | switch (dp->link_train.lt_state) { | 685 | switch (dp->link_train.lt_state) { |
| 712 | case START: | 686 | case START: |
| 713 | exynos_dp_link_start(dp); | 687 | retval = exynos_dp_link_start(dp); |
| 688 | if (retval) | ||
| 689 | dev_err(dp->dev, "LT link start failed!\n"); | ||
| 714 | break; | 690 | break; |
| 715 | case CLOCK_RECOVERY: | 691 | case CLOCK_RECOVERY: |
| 716 | retval = exynos_dp_process_clock_recovery(dp); | 692 | retval = exynos_dp_process_clock_recovery(dp); |
| @@ -729,6 +705,8 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp) | |||
| 729 | return -EREMOTEIO; | 705 | return -EREMOTEIO; |
| 730 | } | 706 | } |
| 731 | } | 707 | } |
| 708 | if (retval) | ||
| 709 | dev_err(dp->dev, "eDP link training failed (%d)\n", retval); | ||
| 732 | 710 | ||
| 733 | return retval; | 711 | return retval; |
| 734 | } | 712 | } |
| @@ -752,19 +730,15 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp, | |||
| 752 | return retval; | 730 | return retval; |
| 753 | } | 731 | } |
| 754 | 732 | ||
| 755 | static int exynos_dp_config_video(struct exynos_dp_device *dp, | 733 | static int exynos_dp_config_video(struct exynos_dp_device *dp) |
| 756 | struct video_info *video_info) | ||
| 757 | { | 734 | { |
| 758 | int retval = 0; | 735 | int retval = 0; |
| 759 | int timeout_loop = 0; | 736 | int timeout_loop = 0; |
| 760 | int done_count = 0; | 737 | int done_count = 0; |
| 761 | 738 | ||
| 762 | exynos_dp_config_video_slave_mode(dp, video_info); | 739 | exynos_dp_config_video_slave_mode(dp); |
| 763 | 740 | ||
| 764 | exynos_dp_set_video_color_format(dp, video_info->color_depth, | 741 | exynos_dp_set_video_color_format(dp); |
| 765 | video_info->color_space, | ||
| 766 | video_info->dynamic_range, | ||
| 767 | video_info->ycbcr_coeff); | ||
| 768 | 742 | ||
| 769 | if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { | 743 | if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { |
| 770 | dev_err(dp->dev, "PLL is not locked yet.\n"); | 744 | dev_err(dp->dev, "PLL is not locked yet.\n"); |
| @@ -852,10 +826,213 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg) | |||
| 852 | { | 826 | { |
| 853 | struct exynos_dp_device *dp = arg; | 827 | struct exynos_dp_device *dp = arg; |
| 854 | 828 | ||
| 855 | dev_err(dp->dev, "exynos_dp_irq_handler\n"); | 829 | enum dp_irq_type irq_type; |
| 830 | |||
| 831 | irq_type = exynos_dp_get_irq_type(dp); | ||
| 832 | switch (irq_type) { | ||
| 833 | case DP_IRQ_TYPE_HP_CABLE_IN: | ||
| 834 | dev_dbg(dp->dev, "Received irq - cable in\n"); | ||
| 835 | schedule_work(&dp->hotplug_work); | ||
| 836 | exynos_dp_clear_hotplug_interrupts(dp); | ||
| 837 | break; | ||
| 838 | case DP_IRQ_TYPE_HP_CABLE_OUT: | ||
| 839 | dev_dbg(dp->dev, "Received irq - cable out\n"); | ||
| 840 | exynos_dp_clear_hotplug_interrupts(dp); | ||
| 841 | break; | ||
| 842 | case DP_IRQ_TYPE_HP_CHANGE: | ||
| 843 | /* | ||
| 844 | * We get these change notifications once in a while, but there | ||
| 845 | * is nothing we can do with them. Just ignore it for now and | ||
| 846 | * only handle cable changes. | ||
| 847 | */ | ||
| 848 | dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n"); | ||
| 849 | exynos_dp_clear_hotplug_interrupts(dp); | ||
| 850 | break; | ||
| 851 | default: | ||
| 852 | dev_err(dp->dev, "Received irq - unknown type!\n"); | ||
| 853 | break; | ||
| 854 | } | ||
| 856 | return IRQ_HANDLED; | 855 | return IRQ_HANDLED; |
| 857 | } | 856 | } |
| 858 | 857 | ||
| 858 | static void exynos_dp_hotplug(struct work_struct *work) | ||
| 859 | { | ||
| 860 | struct exynos_dp_device *dp; | ||
| 861 | int ret; | ||
| 862 | |||
| 863 | dp = container_of(work, struct exynos_dp_device, hotplug_work); | ||
| 864 | |||
| 865 | ret = exynos_dp_detect_hpd(dp); | ||
| 866 | if (ret) { | ||
| 867 | /* Cable has been disconnected, we're done */ | ||
| 868 | return; | ||
| 869 | } | ||
| 870 | |||
| 871 | ret = exynos_dp_handle_edid(dp); | ||
| 872 | if (ret) { | ||
| 873 | dev_err(dp->dev, "unable to handle edid\n"); | ||
| 874 | return; | ||
| 875 | } | ||
| 876 | |||
| 877 | ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, | ||
| 878 | dp->video_info->link_rate); | ||
| 879 | if (ret) { | ||
| 880 | dev_err(dp->dev, "unable to do link train\n"); | ||
| 881 | return; | ||
| 882 | } | ||
| 883 | |||
| 884 | exynos_dp_enable_scramble(dp, 1); | ||
| 885 | exynos_dp_enable_rx_to_enhanced_mode(dp, 1); | ||
| 886 | exynos_dp_enable_enhanced_mode(dp, 1); | ||
| 887 | |||
| 888 | exynos_dp_set_lane_count(dp, dp->video_info->lane_count); | ||
| 889 | exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); | ||
| 890 | |||
| 891 | exynos_dp_init_video(dp); | ||
| 892 | ret = exynos_dp_config_video(dp); | ||
| 893 | if (ret) | ||
| 894 | dev_err(dp->dev, "unable to config video\n"); | ||
| 895 | } | ||
| 896 | |||
| 897 | #ifdef CONFIG_OF | ||
| 898 | static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) | ||
| 899 | { | ||
| 900 | struct device_node *dp_node = dev->of_node; | ||
| 901 | struct exynos_dp_platdata *pd; | ||
| 902 | struct video_info *dp_video_config; | ||
| 903 | |||
| 904 | pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); | ||
| 905 | if (!pd) { | ||
| 906 | dev_err(dev, "memory allocation for pdata failed\n"); | ||
| 907 | return ERR_PTR(-ENOMEM); | ||
| 908 | } | ||
| 909 | dp_video_config = devm_kzalloc(dev, | ||
| 910 | sizeof(*dp_video_config), GFP_KERNEL); | ||
| 911 | |||
| 912 | if (!dp_video_config) { | ||
| 913 | dev_err(dev, "memory allocation for video config failed\n"); | ||
| 914 | return ERR_PTR(-ENOMEM); | ||
| 915 | } | ||
| 916 | pd->video_info = dp_video_config; | ||
| 917 | |||
| 918 | dp_video_config->h_sync_polarity = | ||
| 919 | of_property_read_bool(dp_node, "hsync-active-high"); | ||
| 920 | |||
| 921 | dp_video_config->v_sync_polarity = | ||
| 922 | of_property_read_bool(dp_node, "vsync-active-high"); | ||
| 923 | |||
| 924 | dp_video_config->interlaced = | ||
| 925 | of_property_read_bool(dp_node, "interlaced"); | ||
| 926 | |||
| 927 | if (of_property_read_u32(dp_node, "samsung,color-space", | ||
| 928 | &dp_video_config->color_space)) { | ||
| 929 | dev_err(dev, "failed to get color-space\n"); | ||
| 930 | return ERR_PTR(-EINVAL); | ||
| 931 | } | ||
| 932 | |||
| 933 | if (of_property_read_u32(dp_node, "samsung,dynamic-range", | ||
| 934 | &dp_video_config->dynamic_range)) { | ||
| 935 | dev_err(dev, "failed to get dynamic-range\n"); | ||
| 936 | return ERR_PTR(-EINVAL); | ||
| 937 | } | ||
| 938 | |||
| 939 | if (of_property_read_u32(dp_node, "samsung,ycbcr-coeff", | ||
| 940 | &dp_video_config->ycbcr_coeff)) { | ||
| 941 | dev_err(dev, "failed to get ycbcr-coeff\n"); | ||
| 942 | return ERR_PTR(-EINVAL); | ||
| 943 | } | ||
| 944 | |||
| 945 | if (of_property_read_u32(dp_node, "samsung,color-depth", | ||
| 946 | &dp_video_config->color_depth)) { | ||
| 947 | dev_err(dev, "failed to get color-depth\n"); | ||
| 948 | return ERR_PTR(-EINVAL); | ||
| 949 | } | ||
| 950 | |||
| 951 | if (of_property_read_u32(dp_node, "samsung,link-rate", | ||
| 952 | &dp_video_config->link_rate)) { | ||
| 953 | dev_err(dev, "failed to get link-rate\n"); | ||
| 954 | return ERR_PTR(-EINVAL); | ||
| 955 | } | ||
| 956 | |||
| 957 | if (of_property_read_u32(dp_node, "samsung,lane-count", | ||
| 958 | &dp_video_config->lane_count)) { | ||
| 959 | dev_err(dev, "failed to get lane-count\n"); | ||
| 960 | return ERR_PTR(-EINVAL); | ||
| 961 | } | ||
| 962 | |||
| 963 | return pd; | ||
| 964 | } | ||
| 965 | |||
| 966 | static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) | ||
| 967 | { | ||
| 968 | struct device_node *dp_phy_node; | ||
| 969 | u32 phy_base; | ||
| 970 | |||
| 971 | dp_phy_node = of_find_node_by_name(dp->dev->of_node, "dptx-phy"); | ||
| 972 | if (!dp_phy_node) { | ||
| 973 | dev_err(dp->dev, "could not find dptx-phy node\n"); | ||
| 974 | return -ENODEV; | ||
| 975 | } | ||
| 976 | |||
| 977 | if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) { | ||
| 978 | dev_err(dp->dev, "faild to get reg for dptx-phy\n"); | ||
| 979 | return -EINVAL; | ||
| 980 | } | ||
| 981 | |||
| 982 | if (of_property_read_u32(dp_phy_node, "samsung,enable-mask", | ||
| 983 | &dp->enable_mask)) { | ||
| 984 | dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n"); | ||
| 985 | return -EINVAL; | ||
| 986 | } | ||
| 987 | |||
| 988 | dp->phy_addr = ioremap(phy_base, SZ_4); | ||
| 989 | if (!dp->phy_addr) { | ||
| 990 | dev_err(dp->dev, "failed to ioremap dp-phy\n"); | ||
| 991 | return -ENOMEM; | ||
| 992 | } | ||
| 993 | |||
| 994 | return 0; | ||
| 995 | } | ||
| 996 | |||
| 997 | static void exynos_dp_phy_init(struct exynos_dp_device *dp) | ||
| 998 | { | ||
| 999 | u32 reg; | ||
| 1000 | |||
| 1001 | reg = __raw_readl(dp->phy_addr); | ||
| 1002 | reg |= dp->enable_mask; | ||
| 1003 | __raw_writel(reg, dp->phy_addr); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | static void exynos_dp_phy_exit(struct exynos_dp_device *dp) | ||
| 1007 | { | ||
| 1008 | u32 reg; | ||
| 1009 | |||
| 1010 | reg = __raw_readl(dp->phy_addr); | ||
| 1011 | reg &= ~(dp->enable_mask); | ||
| 1012 | __raw_writel(reg, dp->phy_addr); | ||
| 1013 | } | ||
| 1014 | #else | ||
| 1015 | static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) | ||
| 1016 | { | ||
| 1017 | return NULL; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) | ||
| 1021 | { | ||
| 1022 | return -EINVAL; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | static void exynos_dp_phy_init(struct exynos_dp_device *dp) | ||
| 1026 | { | ||
| 1027 | return; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | static void exynos_dp_phy_exit(struct exynos_dp_device *dp) | ||
| 1031 | { | ||
| 1032 | return; | ||
| 1033 | } | ||
| 1034 | #endif /* CONFIG_OF */ | ||
| 1035 | |||
| 859 | static int __devinit exynos_dp_probe(struct platform_device *pdev) | 1036 | static int __devinit exynos_dp_probe(struct platform_device *pdev) |
| 860 | { | 1037 | { |
| 861 | struct resource *res; | 1038 | struct resource *res; |
| @@ -864,12 +1041,6 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) | |||
| 864 | 1041 | ||
| 865 | int ret = 0; | 1042 | int ret = 0; |
| 866 | 1043 | ||
| 867 | pdata = pdev->dev.platform_data; | ||
| 868 | if (!pdata) { | ||
| 869 | dev_err(&pdev->dev, "no platform data\n"); | ||
| 870 | return -EINVAL; | ||
| 871 | } | ||
| 872 | |||
| 873 | dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), | 1044 | dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), |
| 874 | GFP_KERNEL); | 1045 | GFP_KERNEL); |
| 875 | if (!dp) { | 1046 | if (!dp) { |
| @@ -879,6 +1050,22 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) | |||
| 879 | 1050 | ||
| 880 | dp->dev = &pdev->dev; | 1051 | dp->dev = &pdev->dev; |
| 881 | 1052 | ||
| 1053 | if (pdev->dev.of_node) { | ||
| 1054 | pdata = exynos_dp_dt_parse_pdata(&pdev->dev); | ||
| 1055 | if (IS_ERR(pdata)) | ||
| 1056 | return PTR_ERR(pdata); | ||
| 1057 | |||
| 1058 | ret = exynos_dp_dt_parse_phydata(dp); | ||
| 1059 | if (ret) | ||
| 1060 | return ret; | ||
| 1061 | } else { | ||
| 1062 | pdata = pdev->dev.platform_data; | ||
| 1063 | if (!pdata) { | ||
| 1064 | dev_err(&pdev->dev, "no platform data\n"); | ||
| 1065 | return -EINVAL; | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 882 | dp->clock = devm_clk_get(&pdev->dev, "dp"); | 1069 | dp->clock = devm_clk_get(&pdev->dev, "dp"); |
| 883 | if (IS_ERR(dp->clock)) { | 1070 | if (IS_ERR(dp->clock)) { |
| 884 | dev_err(&pdev->dev, "failed to get clock\n"); | 1071 | dev_err(&pdev->dev, "failed to get clock\n"); |
| @@ -896,50 +1083,29 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) | |||
| 896 | } | 1083 | } |
| 897 | 1084 | ||
| 898 | dp->irq = platform_get_irq(pdev, 0); | 1085 | dp->irq = platform_get_irq(pdev, 0); |
| 899 | if (!dp->irq) { | 1086 | if (dp->irq == -ENXIO) { |
| 900 | dev_err(&pdev->dev, "failed to get irq\n"); | 1087 | dev_err(&pdev->dev, "failed to get irq\n"); |
| 901 | return -ENODEV; | 1088 | return -ENODEV; |
| 902 | } | 1089 | } |
| 903 | 1090 | ||
| 904 | ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, | 1091 | INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug); |
| 905 | "exynos-dp", dp); | ||
| 906 | if (ret) { | ||
| 907 | dev_err(&pdev->dev, "failed to request irq\n"); | ||
| 908 | return ret; | ||
| 909 | } | ||
| 910 | 1092 | ||
| 911 | dp->video_info = pdata->video_info; | 1093 | dp->video_info = pdata->video_info; |
| 912 | if (pdata->phy_init) | ||
| 913 | pdata->phy_init(); | ||
| 914 | |||
| 915 | exynos_dp_init_dp(dp); | ||
| 916 | |||
| 917 | ret = exynos_dp_detect_hpd(dp); | ||
| 918 | if (ret) { | ||
| 919 | dev_err(&pdev->dev, "unable to detect hpd\n"); | ||
| 920 | return ret; | ||
| 921 | } | ||
| 922 | 1094 | ||
| 923 | exynos_dp_handle_edid(dp); | 1095 | if (pdev->dev.of_node) { |
| 924 | 1096 | if (dp->phy_addr) | |
| 925 | ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, | 1097 | exynos_dp_phy_init(dp); |
| 926 | dp->video_info->link_rate); | 1098 | } else { |
| 927 | if (ret) { | 1099 | if (pdata->phy_init) |
| 928 | dev_err(&pdev->dev, "unable to do link train\n"); | 1100 | pdata->phy_init(); |
| 929 | return ret; | ||
| 930 | } | 1101 | } |
| 931 | 1102 | ||
| 932 | exynos_dp_enable_scramble(dp, 1); | 1103 | exynos_dp_init_dp(dp); |
| 933 | exynos_dp_enable_rx_to_enhanced_mode(dp, 1); | ||
| 934 | exynos_dp_enable_enhanced_mode(dp, 1); | ||
| 935 | |||
| 936 | exynos_dp_set_lane_count(dp, dp->video_info->lane_count); | ||
| 937 | exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); | ||
| 938 | 1104 | ||
| 939 | exynos_dp_init_video(dp); | 1105 | ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, |
| 940 | ret = exynos_dp_config_video(dp, dp->video_info); | 1106 | "exynos-dp", dp); |
| 941 | if (ret) { | 1107 | if (ret) { |
| 942 | dev_err(&pdev->dev, "unable to config video\n"); | 1108 | dev_err(&pdev->dev, "failed to request irq\n"); |
| 943 | return ret; | 1109 | return ret; |
| 944 | } | 1110 | } |
| 945 | 1111 | ||
| @@ -953,23 +1119,41 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev) | |||
| 953 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | 1119 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; |
| 954 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | 1120 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); |
| 955 | 1121 | ||
| 956 | if (pdata && pdata->phy_exit) | 1122 | disable_irq(dp->irq); |
| 957 | pdata->phy_exit(); | 1123 | |
| 1124 | if (work_pending(&dp->hotplug_work)) | ||
| 1125 | flush_work(&dp->hotplug_work); | ||
| 1126 | |||
| 1127 | if (pdev->dev.of_node) { | ||
| 1128 | if (dp->phy_addr) | ||
| 1129 | exynos_dp_phy_exit(dp); | ||
| 1130 | } else { | ||
| 1131 | if (pdata->phy_exit) | ||
| 1132 | pdata->phy_exit(); | ||
| 1133 | } | ||
| 958 | 1134 | ||
| 959 | clk_disable_unprepare(dp->clock); | 1135 | clk_disable_unprepare(dp->clock); |
| 960 | 1136 | ||
| 1137 | |||
| 961 | return 0; | 1138 | return 0; |
| 962 | } | 1139 | } |
| 963 | 1140 | ||
| 964 | #ifdef CONFIG_PM_SLEEP | 1141 | #ifdef CONFIG_PM_SLEEP |
| 965 | static int exynos_dp_suspend(struct device *dev) | 1142 | static int exynos_dp_suspend(struct device *dev) |
| 966 | { | 1143 | { |
| 967 | struct platform_device *pdev = to_platform_device(dev); | 1144 | struct exynos_dp_platdata *pdata = dev->platform_data; |
| 968 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | 1145 | struct exynos_dp_device *dp = dev_get_drvdata(dev); |
| 969 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | ||
| 970 | 1146 | ||
| 971 | if (pdata && pdata->phy_exit) | 1147 | if (work_pending(&dp->hotplug_work)) |
| 972 | pdata->phy_exit(); | 1148 | flush_work(&dp->hotplug_work); |
| 1149 | |||
| 1150 | if (dev->of_node) { | ||
| 1151 | if (dp->phy_addr) | ||
| 1152 | exynos_dp_phy_exit(dp); | ||
| 1153 | } else { | ||
| 1154 | if (pdata->phy_exit) | ||
| 1155 | pdata->phy_exit(); | ||
| 1156 | } | ||
| 973 | 1157 | ||
| 974 | clk_disable_unprepare(dp->clock); | 1158 | clk_disable_unprepare(dp->clock); |
| 975 | 1159 | ||
| @@ -978,32 +1162,22 @@ static int exynos_dp_suspend(struct device *dev) | |||
| 978 | 1162 | ||
| 979 | static int exynos_dp_resume(struct device *dev) | 1163 | static int exynos_dp_resume(struct device *dev) |
| 980 | { | 1164 | { |
| 981 | struct platform_device *pdev = to_platform_device(dev); | 1165 | struct exynos_dp_platdata *pdata = dev->platform_data; |
| 982 | struct exynos_dp_platdata *pdata = pdev->dev.platform_data; | 1166 | struct exynos_dp_device *dp = dev_get_drvdata(dev); |
| 983 | struct exynos_dp_device *dp = platform_get_drvdata(pdev); | ||
| 984 | 1167 | ||
| 985 | if (pdata && pdata->phy_init) | 1168 | if (dev->of_node) { |
| 986 | pdata->phy_init(); | 1169 | if (dp->phy_addr) |
| 1170 | exynos_dp_phy_init(dp); | ||
| 1171 | } else { | ||
| 1172 | if (pdata->phy_init) | ||
| 1173 | pdata->phy_init(); | ||
| 1174 | } | ||
| 987 | 1175 | ||
| 988 | clk_prepare_enable(dp->clock); | 1176 | clk_prepare_enable(dp->clock); |
| 989 | 1177 | ||
| 990 | exynos_dp_init_dp(dp); | 1178 | exynos_dp_init_dp(dp); |
| 991 | 1179 | ||
| 992 | exynos_dp_detect_hpd(dp); | 1180 | enable_irq(dp->irq); |
| 993 | exynos_dp_handle_edid(dp); | ||
| 994 | |||
| 995 | exynos_dp_set_link_train(dp, dp->video_info->lane_count, | ||
| 996 | dp->video_info->link_rate); | ||
| 997 | |||
| 998 | exynos_dp_enable_scramble(dp, 1); | ||
| 999 | exynos_dp_enable_rx_to_enhanced_mode(dp, 1); | ||
| 1000 | exynos_dp_enable_enhanced_mode(dp, 1); | ||
| 1001 | |||
| 1002 | exynos_dp_set_lane_count(dp, dp->video_info->lane_count); | ||
| 1003 | exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate); | ||
| 1004 | |||
| 1005 | exynos_dp_init_video(dp); | ||
| 1006 | exynos_dp_config_video(dp, dp->video_info); | ||
| 1007 | 1181 | ||
| 1008 | return 0; | 1182 | return 0; |
| 1009 | } | 1183 | } |
| @@ -1013,6 +1187,12 @@ static const struct dev_pm_ops exynos_dp_pm_ops = { | |||
| 1013 | SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) | 1187 | SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume) |
| 1014 | }; | 1188 | }; |
| 1015 | 1189 | ||
| 1190 | static const struct of_device_id exynos_dp_match[] = { | ||
| 1191 | { .compatible = "samsung,exynos5-dp" }, | ||
| 1192 | {}, | ||
| 1193 | }; | ||
| 1194 | MODULE_DEVICE_TABLE(of, exynos_dp_match); | ||
| 1195 | |||
| 1016 | static struct platform_driver exynos_dp_driver = { | 1196 | static struct platform_driver exynos_dp_driver = { |
| 1017 | .probe = exynos_dp_probe, | 1197 | .probe = exynos_dp_probe, |
| 1018 | .remove = __devexit_p(exynos_dp_remove), | 1198 | .remove = __devexit_p(exynos_dp_remove), |
| @@ -1020,6 +1200,7 @@ static struct platform_driver exynos_dp_driver = { | |||
| 1020 | .name = "exynos-dp", | 1200 | .name = "exynos-dp", |
| 1021 | .owner = THIS_MODULE, | 1201 | .owner = THIS_MODULE, |
| 1022 | .pm = &exynos_dp_pm_ops, | 1202 | .pm = &exynos_dp_pm_ops, |
| 1203 | .of_match_table = of_match_ptr(exynos_dp_match), | ||
| 1023 | }, | 1204 | }, |
| 1024 | }; | 1205 | }; |
| 1025 | 1206 | ||
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h index 57b8a6531c0e..6c567bbf2fb8 100644 --- a/drivers/video/exynos/exynos_dp_core.h +++ b/drivers/video/exynos/exynos_dp_core.h | |||
| @@ -13,6 +13,13 @@ | |||
| 13 | #ifndef _EXYNOS_DP_CORE_H | 13 | #ifndef _EXYNOS_DP_CORE_H |
| 14 | #define _EXYNOS_DP_CORE_H | 14 | #define _EXYNOS_DP_CORE_H |
| 15 | 15 | ||
| 16 | enum dp_irq_type { | ||
| 17 | DP_IRQ_TYPE_HP_CABLE_IN, | ||
| 18 | DP_IRQ_TYPE_HP_CABLE_OUT, | ||
| 19 | DP_IRQ_TYPE_HP_CHANGE, | ||
| 20 | DP_IRQ_TYPE_UNKNOWN, | ||
| 21 | }; | ||
| 22 | |||
| 16 | struct link_train { | 23 | struct link_train { |
| 17 | int eq_loop; | 24 | int eq_loop; |
| 18 | int cr_loop[4]; | 25 | int cr_loop[4]; |
| @@ -29,9 +36,12 @@ struct exynos_dp_device { | |||
| 29 | struct clk *clock; | 36 | struct clk *clock; |
| 30 | unsigned int irq; | 37 | unsigned int irq; |
| 31 | void __iomem *reg_base; | 38 | void __iomem *reg_base; |
| 39 | void __iomem *phy_addr; | ||
| 40 | unsigned int enable_mask; | ||
| 32 | 41 | ||
| 33 | struct video_info *video_info; | 42 | struct video_info *video_info; |
| 34 | struct link_train link_train; | 43 | struct link_train link_train; |
| 44 | struct work_struct hotplug_work; | ||
| 35 | }; | 45 | }; |
| 36 | 46 | ||
| 37 | /* exynos_dp_reg.c */ | 47 | /* exynos_dp_reg.c */ |
| @@ -50,6 +60,8 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, | |||
| 50 | bool enable); | 60 | bool enable); |
| 51 | void exynos_dp_init_analog_func(struct exynos_dp_device *dp); | 61 | void exynos_dp_init_analog_func(struct exynos_dp_device *dp); |
| 52 | void exynos_dp_init_hpd(struct exynos_dp_device *dp); | 62 | void exynos_dp_init_hpd(struct exynos_dp_device *dp); |
| 63 | enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp); | ||
| 64 | void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp); | ||
| 53 | void exynos_dp_reset_aux(struct exynos_dp_device *dp); | 65 | void exynos_dp_reset_aux(struct exynos_dp_device *dp); |
| 54 | void exynos_dp_init_aux(struct exynos_dp_device *dp); | 66 | void exynos_dp_init_aux(struct exynos_dp_device *dp); |
| 55 | int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp); | 67 | int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp); |
| @@ -107,11 +119,7 @@ u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp); | |||
| 107 | void exynos_dp_reset_macro(struct exynos_dp_device *dp); | 119 | void exynos_dp_reset_macro(struct exynos_dp_device *dp); |
| 108 | void exynos_dp_init_video(struct exynos_dp_device *dp); | 120 | void exynos_dp_init_video(struct exynos_dp_device *dp); |
| 109 | 121 | ||
| 110 | void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, | 122 | void exynos_dp_set_video_color_format(struct exynos_dp_device *dp); |
| 111 | u32 color_depth, | ||
| 112 | u32 color_space, | ||
| 113 | u32 dynamic_range, | ||
| 114 | u32 ycbcr_coeff); | ||
| 115 | int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp); | 123 | int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp); |
| 116 | void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, | 124 | void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp, |
| 117 | enum clock_recovery_m_value_type type, | 125 | enum clock_recovery_m_value_type type, |
| @@ -121,8 +129,7 @@ void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type); | |||
| 121 | void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable); | 129 | void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable); |
| 122 | void exynos_dp_start_video(struct exynos_dp_device *dp); | 130 | void exynos_dp_start_video(struct exynos_dp_device *dp); |
| 123 | int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp); | 131 | int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp); |
| 124 | void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, | 132 | void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp); |
| 125 | struct video_info *video_info); | ||
| 126 | void exynos_dp_enable_scrambling(struct exynos_dp_device *dp); | 133 | void exynos_dp_enable_scrambling(struct exynos_dp_device *dp); |
| 127 | void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); | 134 | void exynos_dp_disable_scrambling(struct exynos_dp_device *dp); |
| 128 | 135 | ||
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c index 3f5ca8a0d5ea..29d9d035c73a 100644 --- a/drivers/video/exynos/exynos_dp_reg.c +++ b/drivers/video/exynos/exynos_dp_reg.c | |||
| @@ -19,11 +19,11 @@ | |||
| 19 | #include "exynos_dp_core.h" | 19 | #include "exynos_dp_core.h" |
| 20 | #include "exynos_dp_reg.h" | 20 | #include "exynos_dp_reg.h" |
| 21 | 21 | ||
| 22 | #define COMMON_INT_MASK_1 (0) | 22 | #define COMMON_INT_MASK_1 0 |
| 23 | #define COMMON_INT_MASK_2 (0) | 23 | #define COMMON_INT_MASK_2 0 |
| 24 | #define COMMON_INT_MASK_3 (0) | 24 | #define COMMON_INT_MASK_3 0 |
| 25 | #define COMMON_INT_MASK_4 (0) | 25 | #define COMMON_INT_MASK_4 (HOTPLUG_CHG | HPD_LOST | PLUG) |
| 26 | #define INT_STA_MASK (0) | 26 | #define INT_STA_MASK INT_HPD |
| 27 | 27 | ||
| 28 | void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable) | 28 | void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable) |
| 29 | { | 29 | { |
| @@ -88,7 +88,7 @@ void exynos_dp_init_analog_param(struct exynos_dp_device *dp) | |||
| 88 | void exynos_dp_init_interrupt(struct exynos_dp_device *dp) | 88 | void exynos_dp_init_interrupt(struct exynos_dp_device *dp) |
| 89 | { | 89 | { |
| 90 | /* Set interrupt pin assertion polarity as high */ | 90 | /* Set interrupt pin assertion polarity as high */ |
| 91 | writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL); | 91 | writel(INT_POL1 | INT_POL0, dp->reg_base + EXYNOS_DP_INT_CTL); |
| 92 | 92 | ||
| 93 | /* Clear pending regisers */ | 93 | /* Clear pending regisers */ |
| 94 | writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); | 94 | writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1); |
| @@ -324,7 +324,7 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp) | |||
| 324 | writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); | 324 | writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2); |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | void exynos_dp_init_hpd(struct exynos_dp_device *dp) | 327 | void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp) |
| 328 | { | 328 | { |
| 329 | u32 reg; | 329 | u32 reg; |
| 330 | 330 | ||
| @@ -333,12 +333,38 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp) | |||
| 333 | 333 | ||
| 334 | reg = INT_HPD; | 334 | reg = INT_HPD; |
| 335 | writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); | 335 | writel(reg, dp->reg_base + EXYNOS_DP_INT_STA); |
| 336 | } | ||
| 337 | |||
| 338 | void exynos_dp_init_hpd(struct exynos_dp_device *dp) | ||
| 339 | { | ||
| 340 | u32 reg; | ||
| 341 | |||
| 342 | exynos_dp_clear_hotplug_interrupts(dp); | ||
| 336 | 343 | ||
| 337 | reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); | 344 | reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); |
| 338 | reg &= ~(F_HPD | HPD_CTRL); | 345 | reg &= ~(F_HPD | HPD_CTRL); |
| 339 | writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); | 346 | writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3); |
| 340 | } | 347 | } |
| 341 | 348 | ||
| 349 | enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp) | ||
| 350 | { | ||
| 351 | u32 reg; | ||
| 352 | |||
| 353 | /* Parse hotplug interrupt status register */ | ||
| 354 | reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); | ||
| 355 | |||
| 356 | if (reg & PLUG) | ||
| 357 | return DP_IRQ_TYPE_HP_CABLE_IN; | ||
| 358 | |||
| 359 | if (reg & HPD_LOST) | ||
| 360 | return DP_IRQ_TYPE_HP_CABLE_OUT; | ||
| 361 | |||
| 362 | if (reg & HOTPLUG_CHG) | ||
| 363 | return DP_IRQ_TYPE_HP_CHANGE; | ||
| 364 | |||
| 365 | return DP_IRQ_TYPE_UNKNOWN; | ||
| 366 | } | ||
| 367 | |||
| 342 | void exynos_dp_reset_aux(struct exynos_dp_device *dp) | 368 | void exynos_dp_reset_aux(struct exynos_dp_device *dp) |
| 343 | { | 369 | { |
| 344 | u32 reg; | 370 | u32 reg; |
| @@ -491,7 +517,7 @@ int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp, | |||
| 491 | int i; | 517 | int i; |
| 492 | int retval; | 518 | int retval; |
| 493 | 519 | ||
| 494 | for (i = 0; i < 10; i++) { | 520 | for (i = 0; i < 3; i++) { |
| 495 | /* Clear AUX CH data buffer */ | 521 | /* Clear AUX CH data buffer */ |
| 496 | reg = BUF_CLR; | 522 | reg = BUF_CLR; |
| 497 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); | 523 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); |
| @@ -552,7 +578,7 @@ int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp, | |||
| 552 | else | 578 | else |
| 553 | cur_data_count = count - start_offset; | 579 | cur_data_count = count - start_offset; |
| 554 | 580 | ||
| 555 | for (i = 0; i < 10; i++) { | 581 | for (i = 0; i < 3; i++) { |
| 556 | /* Select DPCD device address */ | 582 | /* Select DPCD device address */ |
| 557 | reg = AUX_ADDR_7_0(reg_addr + start_offset); | 583 | reg = AUX_ADDR_7_0(reg_addr + start_offset); |
| 558 | writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); | 584 | writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); |
| @@ -617,7 +643,7 @@ int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp, | |||
| 617 | cur_data_count = count - start_offset; | 643 | cur_data_count = count - start_offset; |
| 618 | 644 | ||
| 619 | /* AUX CH Request Transaction process */ | 645 | /* AUX CH Request Transaction process */ |
| 620 | for (i = 0; i < 10; i++) { | 646 | for (i = 0; i < 3; i++) { |
| 621 | /* Select DPCD device address */ | 647 | /* Select DPCD device address */ |
| 622 | reg = AUX_ADDR_7_0(reg_addr + start_offset); | 648 | reg = AUX_ADDR_7_0(reg_addr + start_offset); |
| 623 | writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); | 649 | writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0); |
| @@ -700,17 +726,15 @@ int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp, | |||
| 700 | int i; | 726 | int i; |
| 701 | int retval; | 727 | int retval; |
| 702 | 728 | ||
| 703 | for (i = 0; i < 10; i++) { | 729 | for (i = 0; i < 3; i++) { |
| 704 | /* Clear AUX CH data buffer */ | 730 | /* Clear AUX CH data buffer */ |
| 705 | reg = BUF_CLR; | 731 | reg = BUF_CLR; |
| 706 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); | 732 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); |
| 707 | 733 | ||
| 708 | /* Select EDID device */ | 734 | /* Select EDID device */ |
| 709 | retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr); | 735 | retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr); |
| 710 | if (retval != 0) { | 736 | if (retval != 0) |
| 711 | dev_err(dp->dev, "Select EDID device fail!\n"); | ||
| 712 | continue; | 737 | continue; |
| 713 | } | ||
| 714 | 738 | ||
| 715 | /* | 739 | /* |
| 716 | * Set I2C transaction and read data | 740 | * Set I2C transaction and read data |
| @@ -750,7 +774,7 @@ int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp, | |||
| 750 | int retval = 0; | 774 | int retval = 0; |
| 751 | 775 | ||
| 752 | for (i = 0; i < count; i += 16) { | 776 | for (i = 0; i < count; i += 16) { |
| 753 | for (j = 0; j < 100; j++) { | 777 | for (j = 0; j < 3; j++) { |
| 754 | /* Clear AUX CH data buffer */ | 778 | /* Clear AUX CH data buffer */ |
| 755 | reg = BUF_CLR; | 779 | reg = BUF_CLR; |
| 756 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); | 780 | writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL); |
| @@ -1034,24 +1058,20 @@ void exynos_dp_init_video(struct exynos_dp_device *dp) | |||
| 1034 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8); | 1058 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8); |
| 1035 | } | 1059 | } |
| 1036 | 1060 | ||
| 1037 | void exynos_dp_set_video_color_format(struct exynos_dp_device *dp, | 1061 | void exynos_dp_set_video_color_format(struct exynos_dp_device *dp) |
| 1038 | u32 color_depth, | ||
| 1039 | u32 color_space, | ||
| 1040 | u32 dynamic_range, | ||
| 1041 | u32 ycbcr_coeff) | ||
| 1042 | { | 1062 | { |
| 1043 | u32 reg; | 1063 | u32 reg; |
| 1044 | 1064 | ||
| 1045 | /* Configure the input color depth, color space, dynamic range */ | 1065 | /* Configure the input color depth, color space, dynamic range */ |
| 1046 | reg = (dynamic_range << IN_D_RANGE_SHIFT) | | 1066 | reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) | |
| 1047 | (color_depth << IN_BPC_SHIFT) | | 1067 | (dp->video_info->color_depth << IN_BPC_SHIFT) | |
| 1048 | (color_space << IN_COLOR_F_SHIFT); | 1068 | (dp->video_info->color_space << IN_COLOR_F_SHIFT); |
| 1049 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2); | 1069 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2); |
| 1050 | 1070 | ||
| 1051 | /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ | 1071 | /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ |
| 1052 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); | 1072 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3); |
| 1053 | reg &= ~IN_YC_COEFFI_MASK; | 1073 | reg &= ~IN_YC_COEFFI_MASK; |
| 1054 | if (ycbcr_coeff) | 1074 | if (dp->video_info->ycbcr_coeff) |
| 1055 | reg |= IN_YC_COEFFI_ITU709; | 1075 | reg |= IN_YC_COEFFI_ITU709; |
| 1056 | else | 1076 | else |
| 1057 | reg |= IN_YC_COEFFI_ITU601; | 1077 | reg |= IN_YC_COEFFI_ITU601; |
| @@ -1178,8 +1198,7 @@ int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp) | |||
| 1178 | return 0; | 1198 | return 0; |
| 1179 | } | 1199 | } |
| 1180 | 1200 | ||
| 1181 | void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, | 1201 | void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp) |
| 1182 | struct video_info *video_info) | ||
| 1183 | { | 1202 | { |
| 1184 | u32 reg; | 1203 | u32 reg; |
| 1185 | 1204 | ||
| @@ -1190,17 +1209,17 @@ void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp, | |||
| 1190 | 1209 | ||
| 1191 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1210 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1192 | reg &= ~INTERACE_SCAN_CFG; | 1211 | reg &= ~INTERACE_SCAN_CFG; |
| 1193 | reg |= (video_info->interlaced << 2); | 1212 | reg |= (dp->video_info->interlaced << 2); |
| 1194 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1213 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1195 | 1214 | ||
| 1196 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1215 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1197 | reg &= ~VSYNC_POLARITY_CFG; | 1216 | reg &= ~VSYNC_POLARITY_CFG; |
| 1198 | reg |= (video_info->v_sync_polarity << 1); | 1217 | reg |= (dp->video_info->v_sync_polarity << 1); |
| 1199 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1218 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1200 | 1219 | ||
| 1201 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1220 | reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1202 | reg &= ~HSYNC_POLARITY_CFG; | 1221 | reg &= ~HSYNC_POLARITY_CFG; |
| 1203 | reg |= (video_info->h_sync_polarity << 0); | 1222 | reg |= (dp->video_info->h_sync_polarity << 0); |
| 1204 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); | 1223 | writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10); |
| 1205 | 1224 | ||
| 1206 | reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; | 1225 | reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; |
diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h index 1f2f014cfe88..2e9bd0e0b9f2 100644 --- a/drivers/video/exynos/exynos_dp_reg.h +++ b/drivers/video/exynos/exynos_dp_reg.h | |||
| @@ -242,7 +242,8 @@ | |||
| 242 | 242 | ||
| 243 | /* EXYNOS_DP_INT_CTL */ | 243 | /* EXYNOS_DP_INT_CTL */ |
| 244 | #define SOFT_INT_CTRL (0x1 << 2) | 244 | #define SOFT_INT_CTRL (0x1 << 2) |
| 245 | #define INT_POL (0x1 << 0) | 245 | #define INT_POL1 (0x1 << 1) |
| 246 | #define INT_POL0 (0x1 << 0) | ||
| 246 | 247 | ||
| 247 | /* EXYNOS_DP_SYS_CTL_1 */ | 248 | /* EXYNOS_DP_SYS_CTL_1 */ |
| 248 | #define DET_STA (0x1 << 2) | 249 | #define DET_STA (0x1 << 2) |
