diff options
| author | Dave Airlie <airlied@redhat.com> | 2016-04-06 09:57:33 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2016-04-06 09:57:33 +1000 |
| commit | d00b39c17573ece6f5fb1385314877d29f540db8 (patch) | |
| tree | 859eda0522e4a5bbfd727c07ec210686cb799b08 | |
| parent | 85bd5ac371c844f66918dda8c83145a26a55f9f2 (diff) | |
| parent | 7b4b7a8db439dca4342a0424b3d99d878a4e5a12 (diff) | |
| download | linux-d00b39c17573ece6f5fb1385314877d29f540db8.tar.gz linux-d00b39c17573ece6f5fb1385314877d29f540db8.tar.bz2 linux-d00b39c17573ece6f5fb1385314877d29f540db8.zip | |
Merge branch 'drm-next-analogix-dp-v2' of github.com:yakir-Yang/linux into drm-next
This pull request want to land the analogix_dp driver into drm/bridge directory,
which reused the Exynos DP code, and add Rockchip DP support. And those
patches have been:
* 'drm-next-analogix-dp-v2' of github.com:yakir-Yang/linux:
drm: bridge: analogix/dp: Fix the possible dead lock in bridge disable time
drm: bridge: analogix/dp: add panel prepare/unprepare in suspend/resume time
drm: bridge: analogix/dp: add edid modes parse in get_modes method
drm: bridge: analogix/dp: move hpd detect to connector detect function
drm: bridge: analogix/dp: try force hpd after plug in lookup failed
drm: bridge: analogix/dp: add max link rate and lane count limit for RK3288
drm: bridge: analogix/dp: add some rk3288 special registers setting
dt-bindings: add document for rockchip variant of analogix_dp
drm: rockchip: dp: add rockchip platform dp driver
ARM: dts: exynos/dp: remove some properties that deprecated by analogix_dp driver
dt-bindings: add document for analogix display port driver
drm: bridge: analogix/dp: dynamic parse sync_pol & interlace & dynamic_range
drm: bridge: analogix/dp: remove duplicate configuration of link rate and link count
drm: bridge: analogix/dp: fix some obvious code style
drm: bridge: analogix/dp: rename register constants
drm/exynos: dp: rename implementation specific driver part
drm: bridge: analogix/dp: split exynos dp driver to bridge directory
28 files changed, 4106 insertions, 3258 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt new file mode 100644 index 000000000000..4f2ba8c13d92 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt @@ -0,0 +1,52 @@ +Analogix Display Port bridge bindings + +Required properties for dp-controller: + -compatible: + platform specific such as: + * "samsung,exynos5-dp" + * "rockchip,rk3288-dp" + -reg: + physical base address of the controller and length + of memory mapped region. + -interrupts: + interrupt combiner values. + -clocks: + from common clock binding: handle to dp clock. + -clock-names: + from common clock binding: Shall be "dp". + -interrupt-parent: + phandle to Interrupt combiner node. + -phys: + from general PHY binding: the phandle for the PHY device. + -phy-names: + from general PHY binding: Should be "dp". + +Optional properties for dp-controller: + -force-hpd: + Indicate driver need force hpd when hpd detect failed, this + is used for some eDP screen which don't have hpd signal. + -hpd-gpios: + Hotplug detect GPIO. + Indicates which GPIO should be used for hotplug detection + -port@[X]: SoC specific port nodes with endpoint definitions as defined + in Documentation/devicetree/bindings/media/video-interfaces.txt, + please refer to the SoC specific binding document: + * Documentation/devicetree/bindings/display/exynos/exynos_dp.txt + * Documentation/devicetree/bindings/video/analogix_dp-rockchip.txt + +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt +------------------------------------------------------------------------------- + +Example: + + dp-controller { + compatible = "samsung,exynos5-dp"; + reg = <0x145b0000 0x10000>; + interrupts = <10 3>; + interrupt-parent = <&combiner>; + clocks = <&clock 342>; + clock-names = "dp"; + + phys = <&dp_phy>; + phy-names = "dp"; + }; diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt index fe4a7a2dea9c..ade5d8eebf85 100644 --- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt +++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt @@ -1,20 +1,3 @@ -Device-Tree bindings for Samsung Exynos Embedded DisplayPort Transmitter(eDP) - -DisplayPort is industry standard to accommodate the growing board adoption -of digital display technology within the PC and CE industries. -It consolidates the internal and external connection methods to reduce device -complexity and cost. It also supports necessary features for important cross -industry applications and provides performance scalability to enable the next -generation of displays that feature higher color depths, refresh rates, and -display resolutions. - -eDP (embedded display port) device is compliant with Embedded DisplayPort -standard as follows, -- DisplayPort standard 1.1a for Exynos5250 and Exynos5260. -- DisplayPort standard 1.3 for Exynos5422s and Exynos5800. - -eDP resides between FIMD and panel or FIMD and bridge such as LVDS. - The Exynos display port interface should be configured based on the type of panel connected to it. @@ -48,26 +31,6 @@ Required properties for dp-controller: from general PHY binding: the phandle for the PHY device. -phy-names: from general PHY binding: Should be "dp". - -samsung,color-space: - input video data format. - COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2 - -samsung,dynamic-range: - dynamic range for input video data. - VESA = 0, CEA = 1 - -samsung,ycbcr-coeff: - YCbCr co-efficients for input video. - COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1 - -samsung,color-depth: - number of bits per colour component. - COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3 - -samsung,link-rate: - link rate supported by the panel. - LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A - -samsung,lane-count: - number of lanes supported by the panel. - LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4 - - display-timings: timings for the connected panel as described by - Documentation/devicetree/bindings/display/display-timing.txt Optional properties for dp-controller: -interlaced: @@ -83,17 +46,31 @@ Optional properties for dp-controller: Hotplug detect GPIO. Indicates which GPIO should be used for hotplug detection -Video interfaces: - Device node can contain video interface port nodes according to [1]. - The following are properties specific to those nodes: - - endpoint node connected to bridge or panel node: - - remote-endpoint: specifies the endpoint in panel or bridge node. - This node is required in all kinds of exynos dp - to represent the connection between dp and bridge - or dp and panel. - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt + -video interfaces: Device node can contain video interface port + nodes according to [1]. + - display-timings: timings for the connected panel as described by + Documentation/devicetree/bindings/display/panel/display-timing.txt + +For the below properties, please refer to Analogix DP binding document: + * Documentation/devicetree/bindings/display/bridge/analogix_dp.txt + -phys (required) + -phy-names (required) + -hpd-gpios (optional) + force-hpd (optional) + +Deprecated properties for DisplayPort: +-interlaced: deprecated prop that can parsed from drm_display_mode. +-vsync-active-high: deprecated prop that can parsed from drm_display_mode. +-hsync-active-high: deprecated prop that can parsed from drm_display_mode. +-samsung,ycbcr-coeff: deprecated prop that can parsed from drm_display_mode. +-samsung,dynamic-range: deprecated prop that can parsed from drm_display_mode. +-samsung,color-space: deprecated prop that can parsed from drm_display_info. +-samsung,color-depth: deprecated prop that can parsed from drm_display_info. +-samsung,link-rate: deprecated prop that can reading from monitor by dpcd method. +-samsung,lane-count: deprecated prop that can reading from monitor by dpcd method. +-samsung,hpd-gpio: deprecated name for hpd-gpios. + +------------------------------------------------------------------------------- Example: @@ -112,13 +89,6 @@ SOC specific portion: Board Specific portion: dp-controller { - samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; - samsung,color-depth = <1>; - samsung,link-rate = <0x0a>; - samsung,lane-count = <4>; - display-timings { native-mode = <&lcd_timing>; lcd_timing: 1366x768 { @@ -135,18 +105,9 @@ Board Specific portion: }; ports { - port { + port@0 { dp_out: endpoint { - remote-endpoint = <&dp_in>; - }; - }; - }; - - panel { - ... - port { - dp_in: endpoint { - remote-endpoint = <&dp_out>; + remote-endpoint = <&bridge_in>; }; }; }; diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt new file mode 100644 index 000000000000..e832ff98fd61 --- /dev/null +++ b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt @@ -0,0 +1,92 @@ +Rockchip RK3288 specific extensions to the Analogix Display Port +================================ + +Required properties: +- compatible: "rockchip,rk3288-edp"; + +- reg: physical base address of the controller and length + +- clocks: from common clock binding: handle to dp clock. + of memory mapped region. + +- clock-names: from common clock binding: + Required elements: "dp" "pclk" + +- resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + +- pinctrl-names: Names corresponding to the chip hotplug pinctrl states. +- pinctrl-0: pin-control mode. should be <&edp_hpd> + +- reset-names: Must include the name "dp" + +- rockchip,grf: this soc should set GRF regs, so need get grf here. + +- ports: there are 2 port nodes with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + Port 0: contained 2 endpoints, connecting to the output of vop. + Port 1: contained 1 endpoint, connecting to the input of panel. + +For the below properties, please refer to Analogix DP binding document: + * Documentation/devicetree/bindings/drm/bridge/analogix_dp.txt +- phys (required) +- phy-names (required) +- hpd-gpios (optional) +- force-hpd (optional) +------------------------------------------------------------------------------- + +Example: + dp-controller: dp@ff970000 { + compatible = "rockchip,rk3288-dp"; + reg = <0xff970000 0x4000>; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cru SCLK_EDP>, <&cru PCLK_EDP_CTRL>; + clock-names = "dp", "pclk"; + phys = <&dp_phy>; + phy-names = "dp"; + + rockchip,grf = <&grf>; + resets = <&cru 111>; + reset-names = "dp"; + + pinctrl-names = "default"; + pinctrl-0 = <&edp_hpd>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + edp_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + edp_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_edp>; + }; + edp_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_edp>; + }; + }; + + edp_out: port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + edp_out_panel: endpoint { + reg = <0>; + remote-endpoint = <&panel_in_edp> + }; + }; + }; + }; + + pinctrl { + edp { + edp_hpd: edp-hpd { + rockchip,pins = <7 11 RK_FUNC_2 &pcfg_pull_none>; + }; + }; + }; diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts index 8b2acc74aa76..85d819217d17 100644 --- a/arch/arm/boot/dts/exynos5250-arndale.dts +++ b/arch/arm/boot/dts/exynos5250-arndale.dts @@ -124,8 +124,6 @@ &dp { status = "okay"; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <4>; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 0f5dcd418af8..f30c2dbba4f5 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -80,8 +80,6 @@ &dp { samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <4>; diff --git a/arch/arm/boot/dts/exynos5250-snow-common.dtsi b/arch/arm/boot/dts/exynos5250-snow-common.dtsi index 95210ef6a6b5..746808f401e5 100644 --- a/arch/arm/boot/dts/exynos5250-snow-common.dtsi +++ b/arch/arm/boot/dts/exynos5250-snow-common.dtsi @@ -236,12 +236,10 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd>; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <2>; - samsung,hpd-gpio = <&gpx0 7 GPIO_ACTIVE_HIGH>; + hpd-gpios = <&gpx0 7 GPIO_ACTIVE_HIGH>; ports { port@0 { diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index 0f500cb1eb2d..c607bed575d9 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -74,12 +74,10 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd_gpio>; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <1>; - samsung,hpd-gpio = <&gpc3 0 GPIO_ACTIVE_HIGH>; + hpd-gpios = <&gpc3 0 GPIO_ACTIVE_HIGH>; }; &ehci { diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts index 3981ddb25036..7ddb6a066b28 100644 --- a/arch/arm/boot/dts/exynos5420-peach-pit.dts +++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts @@ -157,12 +157,10 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd_gpio>; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x06>; samsung,lane-count = <2>; - samsung,hpd-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>; + hpd-gpios = <&gpx2 6 GPIO_ACTIVE_HIGH>; ports { port@0 { diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts index 0785fedf441e..288817daa16c 100644 --- a/arch/arm/boot/dts/exynos5420-smdk5420.dts +++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts @@ -102,8 +102,6 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd>; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <4>; diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts index 6e9edc1610c4..6ba9aec15485 100644 --- a/arch/arm/boot/dts/exynos5800-peach-pi.dts +++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts @@ -157,8 +157,6 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd_gpio>; samsung,color-space = <0>; - samsung,dynamic-range = <0>; - samsung,ycbcr-coeff = <0>; samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <2>; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 27e2022de89d..efd94e00c3e5 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -40,4 +40,6 @@ config DRM_PARADE_PS8622 ---help--- Parade eDP-LVDS bridge chip driver. +source "drivers/gpu/drm/bridge/analogix/Kconfig" + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index f13c33d67c03..ff821f4b5833 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o +obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig new file mode 100644 index 000000000000..80f286fa3a69 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -0,0 +1,3 @@ +config DRM_ANALOGIX_DP + tristate + depends on DRM diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile new file mode 100644 index 000000000000..cd4010ba6890 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -0,0 +1,2 @@ +analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o +obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c new file mode 100644 index 000000000000..7699597070a1 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -0,0 +1,1430 @@ +/* +* Analogix DP (Display Port) core interface driver. +* +* Copyright (C) 2012 Samsung Electronics Co., Ltd. +* Author: Jingoo Han <jg1.han@samsung.com> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/component.h> +#include <linux/phy/phy.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_panel.h> + +#include <drm/bridge/analogix_dp.h> + +#include "analogix_dp_core.h" + +#define to_dp(nm) container_of(nm, struct analogix_dp_device, nm) + +struct bridge_init { + struct i2c_client *client; + struct device_node *node; +}; + +static void analogix_dp_init_dp(struct analogix_dp_device *dp) +{ + analogix_dp_reset(dp); + + analogix_dp_swreset(dp); + + analogix_dp_init_analog_param(dp); + analogix_dp_init_interrupt(dp); + + /* SW defined function Normal operation */ + analogix_dp_enable_sw_function(dp); + + analogix_dp_config_interrupt(dp); + analogix_dp_init_analog_func(dp); + + analogix_dp_init_hpd(dp); + analogix_dp_init_aux(dp); +} + +static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) +{ + int timeout_loop = 0; + + while (timeout_loop < DP_TIMEOUT_LOOP_COUNT) { + if (analogix_dp_get_plug_in_status(dp) == 0) + return 0; + + timeout_loop++; + usleep_range(10, 11); + } + + /* + * Some edp screen do not have hpd signal, so we can't just + * return failed when hpd plug in detect failed, DT property + * "force-hpd" would indicate whether driver need this. + */ + if (!dp->force_hpd) + return -ETIMEDOUT; + + /* + * The eDP TRM indicate that if HPD_STATUS(RO) is 0, AUX CH + * will not work, so we need to give a force hpd action to + * set HPD_STATUS manually. + */ + dev_dbg(dp->dev, "failed to get hpd plug status, try to force hpd\n"); + + analogix_dp_force_hpd(dp); + + if (analogix_dp_get_plug_in_status(dp) != 0) { + dev_err(dp->dev, "failed to get hpd plug in status\n"); + return -EINVAL; + } + + dev_dbg(dp->dev, "success to get plug in status after force hpd\n"); + + return 0; +} + +static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data) +{ + int i; + unsigned char sum = 0; + + for (i = 0; i < EDID_BLOCK_LENGTH; i++) + sum = sum + edid_data[i]; + + return sum; +} + +static int analogix_dp_read_edid(struct analogix_dp_device *dp) +{ + unsigned char *edid = dp->edid; + unsigned int extend_block = 0; + unsigned char sum; + unsigned char test_vector; + int retval; + + /* + * EDID device address is 0x50. + * However, if necessary, you must have set upper address + * into E-EDID in I2C device, 0x30. + */ + + /* Read Extension Flag, Number of 128-byte EDID extension blocks */ + retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, + EDID_EXTENSION_FLAG, + &extend_block); + if (retval) + return retval; + + if (extend_block > 0) { + dev_dbg(dp->dev, "EDID data includes a single extension!\n"); + + /* Read EDID data */ + retval = analogix_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, + &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = analogix_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + /* Read additional EDID data */ + retval = analogix_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, + EDID_BLOCK_LENGTH, + EDID_BLOCK_LENGTH, + &edid[EDID_BLOCK_LENGTH]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = analogix_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST, + &test_vector); + if (test_vector & DP_TEST_LINK_EDID_READ) { + analogix_dp_write_byte_to_dpcd(dp, + DP_TEST_EDID_CHECKSUM, + edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); + analogix_dp_write_byte_to_dpcd(dp, + DP_TEST_RESPONSE, + DP_TEST_EDID_CHECKSUM_WRITE); + } + } else { + dev_info(dp->dev, "EDID data does not include any extensions.\n"); + + /* Read EDID data */ + retval = analogix_dp_read_bytes_from_i2c(dp, + I2C_EDID_DEVICE_ADDR, EDID_HEADER_PATTERN, + EDID_BLOCK_LENGTH, &edid[EDID_HEADER_PATTERN]); + if (retval != 0) { + dev_err(dp->dev, "EDID Read failed!\n"); + return -EIO; + } + sum = analogix_dp_calc_edid_check_sum(edid); + if (sum != 0) { + dev_err(dp->dev, "EDID bad checksum!\n"); + return -EIO; + } + + analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST, + &test_vector); + if (test_vector & DP_TEST_LINK_EDID_READ) { + analogix_dp_write_byte_to_dpcd(dp, + DP_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]); + analogix_dp_write_byte_to_dpcd(dp, + DP_TEST_RESPONSE, DP_TEST_EDID_CHECKSUM_WRITE); + } + } + + dev_dbg(dp->dev, "EDID Read success!\n"); + return 0; +} + +static int analogix_dp_handle_edid(struct analogix_dp_device *dp) +{ + u8 buf[12]; + int i; + int retval; + + /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */ + retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV, 12, buf); + if (retval) + return retval; + + /* Read EDID */ + for (i = 0; i < 3; i++) { + retval = analogix_dp_read_edid(dp); + if (!retval) + break; + } + + return retval; +} + +static void +analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp, + bool enable) +{ + u8 data; + + analogix_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data); + + if (enable) + analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET, + DP_LANE_COUNT_ENHANCED_FRAME_EN | + DPCD_LANE_COUNT_SET(data)); + else + analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET, + DPCD_LANE_COUNT_SET(data)); +} + +static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp) +{ + u8 data; + int retval; + + analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data); + retval = DPCD_ENHANCED_FRAME_CAP(data); + + return retval; +} + +static void analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp) +{ + u8 data; + + data = analogix_dp_is_enhanced_mode_available(dp); + analogix_dp_enable_rx_to_enhanced_mode(dp, data); + analogix_dp_enable_enhanced_mode(dp, data); +} + +static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) +{ + analogix_dp_set_training_pattern(dp, DP_NONE); + + analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); +} + +static void +analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp, + int pre_emphasis, int lane) +{ + switch (lane) { + case 0: + analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis); + break; + case 1: + analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis); + break; + + case 2: + analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis); + break; + + case 3: + analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis); + break; + } +} + +static int analogix_dp_link_start(struct analogix_dp_device *dp) +{ + u8 buf[4]; + int lane, lane_count, pll_tries, retval; + + lane_count = dp->link_train.lane_count; + + dp->link_train.lt_state = CLOCK_RECOVERY; + dp->link_train.eq_loop = 0; + + for (lane = 0; lane < lane_count; lane++) + dp->link_train.cr_loop[lane] = 0; + + /* Set link rate and count as you want to establish*/ + analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate); + analogix_dp_set_lane_count(dp, dp->link_train.lane_count); + + /* Setup RX configuration */ + buf[0] = dp->link_train.link_rate; + buf[1] = dp->link_train.lane_count; + retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET, 2, buf); + if (retval) + return retval; + + /* Set TX pre-emphasis to minimum */ + for (lane = 0; lane < lane_count; lane++) + analogix_dp_set_lane_lane_pre_emphasis(dp, + PRE_EMPHASIS_LEVEL_0, lane); + + /* Wait for PLL lock */ + pll_tries = 0; + while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { + dev_err(dp->dev, "Wait for PLL lock timed out\n"); + return -ETIMEDOUT; + } + + pll_tries++; + usleep_range(90, 120); + } + + /* Set training pattern 1 */ + analogix_dp_set_training_pattern(dp, TRAINING_PTN1); + + /* Set RX training pattern */ + retval = analogix_dp_write_byte_to_dpcd(dp, + DP_TRAINING_PATTERN_SET, + DP_LINK_SCRAMBLING_DISABLE |
