diff --git a/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml b/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml new file mode 100644 index 0000000000000..e4c00f6916107 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/lontium,lt8712sx.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/lontium,lt8712sx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lontium LT8712SX/LT8713SX eDP to HDMI Bridge + +maintainers: + - Chen Jiali + +properties: + compatible: + enum: + - lontium,lt8712sx + - lontium,lt8713sx + + reg: + maxItems: 1 + + cec-port-names: + $ref: /schemas/types.yaml#/definitions/string-array + description: + HDMI connector port names used to create named CEC notifiers for boards + that route multiple HDMI CEC lines to GPIO CEC adapters. + minItems: 1 + maxItems: 2 + uniqueItems: true + + enable-gpios: + maxItems: 1 + + power-gpios: + maxItems: 1 + + pinctrl-0: true + + pinctrl-names: + items: + - const: default + + reset-gpios: + maxItems: 1 + + vdd-supply: + description: Power supply for the bridge. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: eDP input port. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: HDMI output port. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hdmi-bridge@4f { + compatible = "lontium,lt8712sx"; + reg = <0x4f>; + cec-port-names = "HDMI0", "HDMI1"; + vdd-supply = <&vcc_3v3>; + reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_in: endpoint { + remote-endpoint = <&edp_out>; + }; + }; + + port@1 { + reg = <1>; + + bridge_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml b/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml index 582c6c9cae48d..64d0a948e56b8 100644 --- a/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml +++ b/Documentation/devicetree/bindings/media/cec/cec-gpio.yaml @@ -40,6 +40,13 @@ properties: GPIO that the 5V line is connected to. Used for debugging changes on the 5V line. + port-name: + $ref: /schemas/types.yaml#/definitions/string + description: + HDMI connector port name used with hdmi-phandle to match this GPIO CEC + adapter to a named HDMI controller notifier when that controller exposes + multiple CEC-capable connectors. + required: - compatible - cec-gpios @@ -60,6 +67,13 @@ allOf: properties: hdmi-phandle: false + - if: + required: + - port-name + then: + required: + - hdmi-phandle + unevaluatedProperties: false examples: diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts index ce445191c5322..0fc7c6fa16882 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64-rpi-cm5-io.dts @@ -11,81 +11,22 @@ model = "Radxa CM-Q64 Raspberry Pi Compute Module 5 IO Board"; compatible = "radxa,cm-q64-rpi-cm5-io", "radxa,cm-q64", "qcom,qcm6490"; - fan_pwm: fan-pwm { - compatible = "pwm-gpio"; - gpios = <&tlmm 106 GPIO_ACTIVE_HIGH>; - pinctrl-0 = <&fan_pwm_cpu>; + m2_3v3: regulator-m2-3v3 { + compatible = "regulator-fixed"; + regulator-name = "m2_3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; pinctrl-names = "default"; - #pwm-cells = <3>; - }; - - pwm_fan: pwm-fan { - pinctrl-0 = <&fan_tacho>; - pinctrl-names = "default"; - compatible = "pwm-fan"; - pwms = <&fan_pwm 0 20000 0>; - fan-supply = <&vcc_3v3>; - interrupt-parent = <&pm8350c_gpios>; - interrupts = <5 IRQ_TYPE_EDGE_FALLING>; - #cooling-cells = <2>; - cooling-levels = <0 120 150 180 210 240 255>; - }; -}; - -&pm8350c_gpios { - gpio-line-names = "", /* 1 */ - "", - "", - "", - "FAN_TACHO_IN", - "", - "LCD_BLEN", - "LCD_BLPWM", - ""; - - fan_tacho: fan-tacho-state { - pins = "gpio5"; - function = PMIC_GPIO_FUNC_NORMAL; - bias-disable; - power-source = <1>; + pinctrl-0 = <&pcieb_pwr_en>; + gpio = <&tlmm 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + vin-supply = <&vdd_5v_dcin>; }; }; - -&thermal_zones { - cpuss0-thermal { - trips { - cpuss0_active: trip-point2 { - temperature = <60000>; - hysteresis = <2000>; - type = "active"; - }; - }; - - cooling-maps { - map2 { - trip = <&cpuss0_active>; - cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; - - cpuss1-thermal { - trips { - cpuss1_active: trip-point2 { - temperature = <60000>; - hysteresis = <2000>; - type = "active"; - }; - }; - - cooling-maps { - map2 { - trip = <&cpuss1_active>; - cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; - }; - }; - }; +&pcie1 { + vddpe-3v3-supply = <&m2_3v3>; + vdda-supply = <&vreg_l6b_1p2>; }; &tlmm { @@ -183,10 +124,9 @@ /* GPIO_172 ~ GPIO_174 */ "", "", ""; - fan_pwm_cpu: fan-ctr-pwm-state { - pins = "gpio106"; + pcieb_pwr_en: pinctrl-pcie1-pwr-en { + pins = "gpio10"; function = "gpio"; bias-disable; - drive-strength = <16>; }; }; diff --git a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi index 86ef62c5dc9bd..519104267c803 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs6490-radxa-cm-q64.dtsi @@ -45,6 +45,26 @@ stdout-path = "serial0:115200n8"; }; + fan_pwm: fan-pwm { + compatible = "pwm-gpio"; + gpios = <&tlmm 108 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&fan_pwm_cpu>; + pinctrl-names = "default"; + #pwm-cells = <3>; + }; + + pwm_fan: pwm-fan { + pinctrl-0 = <&fan_tacho>; + pinctrl-names = "default"; + compatible = "pwm-fan"; + pwms = <&fan_pwm 0 20000 0>; + fan-supply = <&vcc_3v3>; + interrupt-parent = <&pm8350c_gpios>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + #cooling-cells = <2>; + cooling-levels = <0 31 63 95 127 159 191 223 255>; + }; + wcd938x: audio-codec { compatible = "qcom,wcd9380-codec"; @@ -143,6 +163,26 @@ }; }; + /* HDMI0 CEC via GPIO_45 (HDMI0_CEC_CPU), bit-banged through TXS0108 level shifter */ + cec-0 { + compatible = "cec-gpio"; + cec-gpios = <&tlmm 45 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + hdmi-phandle = <<8712sx>; + port-name = "HDMI0"; + pinctrl-0 = <&cec0_gpio_default>; + pinctrl-names = "default"; + }; + + /* HDMI1 CEC via GPIO_46 (HDMI1_CEC_CPU), bit-banged through TXS0108 level shifter */ + cec-1 { + compatible = "cec-gpio"; + cec-gpios = <&tlmm 46 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + hdmi-phandle = <<8712sx>; + port-name = "HDMI1"; + pinctrl-0 = <&cec1_gpio_default>; + pinctrl-names = "default"; + }; + leds { compatible = "gpio-leds"; @@ -266,8 +306,6 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; vin-supply = <&vreg_l7b_2p96>; - - regulator-always-on; }; vbus: regulator-vbus { @@ -278,11 +316,31 @@ vin-supply = <&vcc_3v3>; }; + vdd_5v_dcin: regulator-5v-dcin { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v_dcin"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + regulator-always-on; + }; + + vdd_5v_in: regulator-5v-in { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v_in"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vdd_5v_dcin>; + + regulator-always-on; + }; + vph_pwr: regulator-vph-pwr { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; + regulator-min-microvolt = <4300000>; + regulator-max-microvolt = <4300000>; + vin-supply = <&vdd_5v_in>; regulator-always-on; }; @@ -430,6 +488,18 @@ RPMH_REGULATOR_MODE_HPM>; }; + vreg_l5c_1p62: ldo5 { + regulator-name = "vreg_l5c_1p62"; + regulator-min-microvolt = <1620000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + regulator-allow-set-load; + regulator-allowed-modes = ; + + regulator-always-on; + }; + vreg_l6c_2p96: ldo6 { regulator-name = "vreg_l6c_2p96"; regulator-min-microvolt = <1650000>; @@ -508,6 +578,11 @@ lt8712sx: hdmi-edp-bridge@4f { compatible = "lontium,lt8712sx"; reg = <0x4f>; + cec-port-names = "HDMI0", "HDMI1"; + vdd-supply = <&vcc_3v3s>; + reset-gpios = <&tlmm 110 GPIO_ACTIVE_LOW>; + pinctrl-0 = <<8712sx_reset_default>; + pinctrl-names = "default"; ports { #address-cells = <1>; @@ -541,6 +616,7 @@ rtc: rtc@68 { compatible = "st,m41t11"; reg = <0x68>; + vcc-supply = <&vcc_3v3>; }; }; @@ -594,6 +670,9 @@ }; &pcie0 { + vddpe-3v3-supply = <&vcc_3v3>; + vdda-supply = <&vreg_l6b_1p2>; + perst-gpios = <&tlmm 87 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 89 GPIO_ACTIVE_HIGH>; @@ -690,12 +769,19 @@ "", "", "", - "", + "FAN_TACHO_IN", "", "LCD_BLEN", "LCD_BLPWM", ""; + fan_tacho: fan-tacho-state { + pins = "gpio5"; + function = PMIC_GPIO_FUNC_NORMAL; + bias-disable; + power-source = <1>; + }; + lcd_bl_en: lcd-bl-en-state { pins = "gpio7"; function = PMIC_GPIO_FUNC_NORMAL; @@ -902,7 +988,242 @@ }; }; +&thermal_zones { + cpuss0-thermal { + polling-delay-passive = <100>; + + trips { + cpuss0_alert1: trip-point1 { + temperature = <65000>; + hysteresis = <10000>; + type = "passive"; + }; + + cpuss0_alert2: trip-point2 { + temperature = <80000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpuss0_alert1>; + cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&idle_cooling0 0 0>, + <&idle_cooling1 0 0>, + <&idle_cooling2 0 0>, + <&idle_cooling3 0 0>; + }; + + map1 { + trip = <&cpuss0_alert2>; + cooling-device = <&idle_cooling0 60 90>, + <&idle_cooling1 60 90>, + <&idle_cooling2 60 90>, + <&idle_cooling3 60 90>; + }; + }; + }; + + cpuss1-thermal { + polling-delay-passive = <100>; + + trips { + cpuss1_alert1: trip-point1 { + temperature = <65000>; + hysteresis = <10000>; + type = "passive"; + }; + + cpuss1_alert2: trip-point2 { + temperature = <80000>; + hysteresis = <5000>; + type = "passive"; + }; + }; + cooling-maps { + map0 { + trip = <&cpuss1_alert1>; + cooling-device = <&pwm_fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&idle_cooling4 0 0>, + <&idle_cooling5 0 0>, + <&idle_cooling6 0 0>, + <&idle_cooling7 0 0>; + }; + + map1 { + trip = <&cpuss1_alert2>; + cooling-device = <&idle_cooling4 40 90>, + <&idle_cooling5 60 90>, + <&idle_cooling6 60 90>, + <&idle_cooling7 60 90>; + }; + }; + }; + + gpuss0-thermal { + cooling-maps { + map0 { + trip = <&gpuss0_alert0>; + cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpuss1-thermal { + cooling-maps { + map0 { + trip = <&gpuss1_alert0>; + cooling-device = <&gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; +}; + +&gpuss0_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&gpuss1_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu0_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu1_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu2_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu3_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu4_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu5_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu6_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu7_alert0 { + temperature = <75000>; + hysteresis = <5000>; +}; + +&cpu0 { + idle_cooling0: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu1 { + idle_cooling1: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu2 { + idle_cooling2: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu3 { + idle_cooling3: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu4 { + idle_cooling4: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu5 { + idle_cooling5: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu6 { + idle_cooling6: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + +&cpu7 { + idle_cooling7: thermal-idle { + #cooling-cells = <2>; + duration-us = <20000>; + exit-latency-us = <1500>; + }; +}; + &tlmm { + fan_pwm_cpu: fan-ctr-pwm-state { + pins = "gpio108"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + lt8712sx_reset_default: lt8712sx-reset-default-state { + pins = "gpio110"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + cec0_gpio_default: cec0-gpio-default-state { + pins = "gpio45"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + cec1_gpio_default: cec1-gpio-default-state { + pins = "gpio46"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + pcie0_reset_n: pcie0-reset-n-state { pins = "gpio87"; function = "gpio"; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index b270ee1992189..b722992f0c5a7 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -194,6 +194,7 @@ config DRM_LONTIUM_LT9611UXC config DRM_LONTIUM_LT8712SX tristate "Lontium LT8712SX DP/eDP to HDMI bridge" depends on OF + select CEC_CORE if CEC_NOTIFIER select DRM_KMS_HELPER select REGMAP_I2C help diff --git a/drivers/gpu/drm/bridge/lontium-lt8712sx.c b/drivers/gpu/drm/bridge/lontium-lt8712sx.c index a2da205c7f4e7..d028f12335885 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8712sx.c +++ b/drivers/gpu/drm/bridge/lontium-lt8712sx.c @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -25,6 +27,8 @@ #include #include +#include + #define LT8712SX_PAGE_SIZE 256 #define LT8712SX_MAIN_FW_SIZE SZ_64K #define LT8712SX_MAIN_FW_PAYLOAD_SIZE (LT8712SX_MAIN_FW_SIZE - 1) @@ -44,6 +48,8 @@ #define LT8712SX_STATUS_POLL_DELAY_MS 50 #define LT8712SX_STATUS_POLL_RETRIES 50 +#define LT8712SX_MAX_CEC_PORTS 2 + struct lt8712sx_info { unsigned int connector_type; const char *firmware_name; @@ -85,6 +91,11 @@ struct lt8712sx { struct gpio_desc *enable_gpio; struct mutex lock; bool powered; + + const char *cec_port_names[LT8712SX_MAX_CEC_PORTS]; + struct cec_notifier *cec_notifiers[LT8712SX_MAX_CEC_PORTS]; + unsigned int cec_notifier_count; + bool cec_port_names_initialized; }; static inline struct lt8712sx *bridge_to_lt8712sx(struct drm_bridge *bridge) @@ -98,6 +109,14 @@ static inline struct lt8712sx *connector_to_lt8712sx( return container_of(connector, struct lt8712sx, connector); } +static void lt8712sx_set_phys_addr(struct lt8712sx *lt8712sx, u16 pa) +{ + unsigned int i; + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) + cec_notifier_set_phys_addr(lt8712sx->cec_notifiers[i], pa); +} + static const struct regmap_config lt8712sx_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -231,7 +250,7 @@ static int lt8712sx_power_on(struct lt8712sx *lt8712sx) gpiod_set_value_cansleep(lt8712sx->power_gpio, 1); if (lt8712sx->reset_gpio) - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); lt8712sx->powered = true; @@ -250,7 +269,7 @@ static void lt8712sx_power_off_action(void *data) gpiod_set_value_cansleep(lt8712sx->enable_gpio, 0); if (lt8712sx->reset_gpio) - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); if (lt8712sx->power_gpio) gpiod_set_value_cansleep(lt8712sx->power_gpio, 0); @@ -268,12 +287,12 @@ static int lt8712sx_hw_reset(struct lt8712sx *lt8712sx) if (!lt8712sx->reset_gpio) return 0; - gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); - msleep(5); gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); - msleep(5); + msleep(100); gpiod_set_value_cansleep(lt8712sx->reset_gpio, 1); - msleep(5); + msleep(100); + gpiod_set_value_cansleep(lt8712sx->reset_gpio, 0); + msleep(100); return 0; } @@ -846,6 +865,7 @@ static int lt8712sx_connector_get_modes(struct drm_connector *connector) { struct lt8712sx *lt8712sx = connector_to_lt8712sx(connector); const struct drm_edid *drm_edid; + u16 phys_addr; int ret; if (lt8712sx->next_bridge->ops & DRM_BRIDGE_OP_EDID) { @@ -863,12 +883,17 @@ static int lt8712sx_connector_get_modes(struct drm_connector *connector) * If the downstream HDMI connector does not expose DDC to the SoC, * keep the same no-EDID fallback used by simple-bridge. */ + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); ret = drm_add_modes_noedid(connector, 1920, 1200); drm_set_preferred_mode(connector, 1024, 768); return ret; } ret = drm_edid_connector_add_modes(connector); + + phys_addr = connector->display_info.source_physical_address; + lt8712sx_set_phys_addr(lt8712sx, phys_addr); + drm_edid_free(drm_edid); return ret; @@ -882,8 +907,14 @@ static enum drm_connector_status lt8712sx_connector_detect(struct drm_connector *connector, bool force) { struct lt8712sx *lt8712sx = connector_to_lt8712sx(connector); + enum drm_connector_status status; - return drm_bridge_detect(lt8712sx->next_bridge, connector); + status = drm_bridge_detect(lt8712sx->next_bridge, connector); + + if (status != connector_status_connected) + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); + + return status; } static const struct drm_connector_funcs lt8712sx_con_funcs = { @@ -895,6 +926,103 @@ static const struct drm_connector_funcs lt8712sx_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static void lt8712sx_cec_notifiers_unregister(void *data); + +static int lt8712sx_init_cec_port_names(struct lt8712sx *lt8712sx) +{ + struct device *dev = lt8712sx->dev; + int count; + + if (!device_property_present(dev, "cec-port-names")) { + lt8712sx->cec_notifier_count = 1; + lt8712sx->cec_port_names_initialized = true; + return 0; + } + + if (lt8712sx->cec_port_names_initialized) + return 0; + + count = device_property_string_array_count(dev, "cec-port-names"); + if (count < 0) + return dev_err_probe(dev, count, + "failed to read CEC port names\n"); + + if (count == 0 || count > LT8712SX_MAX_CEC_PORTS) + return dev_err_probe(dev, -EINVAL, + "invalid number of CEC port names\n"); + + count = device_property_read_string_array(dev, "cec-port-names", + lt8712sx->cec_port_names, count); + if (count < 0) + return dev_err_probe(dev, count, + "failed to read CEC port names\n"); + + if (count == 2 && + !strcmp(lt8712sx->cec_port_names[0], lt8712sx->cec_port_names[1])) + return dev_err_probe(dev, -EINVAL, + "duplicate CEC port names\n"); + + lt8712sx->cec_notifier_count = count; + lt8712sx->cec_port_names_initialized = true; + + return 0; +} + +static int lt8712sx_register_cec_notifiers(struct lt8712sx *lt8712sx, + struct drm_connector *connector) +{ + struct cec_connector_info conn_info; + unsigned int i; + int ret; + + ret = lt8712sx_init_cec_port_names(lt8712sx); + if (ret) + return ret; + + cec_fill_conn_info_from_drm(&conn_info, connector); + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) { + if (lt8712sx->cec_notifiers[i]) + continue; + + lt8712sx->cec_notifiers[i] = + cec_notifier_conn_register(lt8712sx->dev, + lt8712sx->cec_port_names[i], + &conn_info); + if (!lt8712sx->cec_notifiers[i]) { + DRM_ERROR("Failed to register CEC notifier\n"); + ret = -ENOMEM; + goto unregister_notifiers; + } + } + + lt8712sx_set_phys_addr(lt8712sx, connector->display_info.source_physical_address); + + return 0; + +unregister_notifiers: + while (i--) { + cec_notifier_conn_unregister(lt8712sx->cec_notifiers[i]); + lt8712sx->cec_notifiers[i] = NULL; + } + lt8712sx->cec_notifier_count = 0; + + return ret; +} + +static int lt8712sx_init_cec_notifiers(struct lt8712sx *lt8712sx) +{ + int ret; + + ret = lt8712sx_init_cec_port_names(lt8712sx); + if (ret) + return ret; + + return devm_add_action_or_reset(lt8712sx->dev, + lt8712sx_cec_notifiers_unregister, + lt8712sx); +} + static int lt8712sx_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) @@ -927,6 +1055,34 @@ static int lt8712sx_attach(struct drm_bridge *bridge, return 0; } +static void +lt8712sx_hpd(struct drm_bridge *bridge, struct drm_connector *connector, + enum drm_connector_status status) +{ + struct lt8712sx *lt8712sx = bridge_to_lt8712sx(bridge); + + if (status != connector_status_connected) { + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); + return; + } + + if (lt8712sx_register_cec_notifiers(lt8712sx, connector)) + lt8712sx_set_phys_addr(lt8712sx, CEC_PHYS_ADDR_INVALID); +} + +static void lt8712sx_cec_notifiers_unregister(void *data) +{ + struct lt8712sx *lt8712sx = data; + unsigned int i; + + for (i = 0; i < lt8712sx->cec_notifier_count; i++) { + cec_notifier_conn_unregister(lt8712sx->cec_notifiers[i]); + lt8712sx->cec_notifiers[i] = NULL; + } + + lt8712sx->cec_notifier_count = 0; +} + static void lt8712sx_enable(struct drm_bridge *bridge) { struct lt8712sx *lt8712sx = bridge_to_lt8712sx(bridge); @@ -945,6 +1101,7 @@ static void lt8712sx_disable(struct drm_bridge *bridge) static const struct drm_bridge_funcs lt8712sx_bridge_funcs = { .attach = lt8712sx_attach, + .hpd_notify = lt8712sx_hpd, .enable = lt8712sx_enable, .disable = lt8712sx_disable, }; @@ -1005,7 +1162,7 @@ static int lt8712sx_probe(struct i2c_client *client) "failed to get power GPIO\n"); lt8712sx->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(lt8712sx->reset_gpio)) return dev_err_probe(dev, PTR_ERR(lt8712sx->reset_gpio), "failed to get reset GPIO\n"); @@ -1026,8 +1183,14 @@ static int lt8712sx_probe(struct i2c_client *client) lt8712sx_try_optional_firmware(lt8712sx); + ret = lt8712sx_init_cec_notifiers(lt8712sx); + if (ret) + return ret; + lt8712sx->bridge.of_node = dev->of_node; + lt8712sx->bridge.ops = DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER; lt8712sx->bridge.type = lt8712sx->info->connector_type; + lt8712sx->bridge.hdmi_cec_dev = dev; return devm_drm_bridge_add(dev, <8712sx->bridge); } diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index 842555ed42c71..a42a6f346f846 100644 --- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,7 @@ static int cec_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device *hdmi_dev; + const char *port_name = NULL; struct cec_gpio *cec; u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; int ret; @@ -184,8 +186,14 @@ static int cec_gpio_probe(struct platform_device *pdev) hdmi_dev = cec_notifier_parse_hdmi_phandle(dev); if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER) return PTR_ERR(hdmi_dev); - if (IS_ERR(hdmi_dev)) + if (IS_ERR(hdmi_dev)) { caps |= CEC_CAP_PHYS_ADDR; + } else if (device_property_present(dev, "port-name")) { + caps |= CEC_CAP_CONNECTOR_INFO; + ret = device_property_read_string(dev, "port-name", &port_name); + if (ret) + return ret; + } cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); if (!cec) @@ -242,7 +250,7 @@ static int cec_gpio_probe(struct platform_device *pdev) } if (!IS_ERR(hdmi_dev)) { - cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, + cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, port_name, cec->adap); if (!cec->notifier) { ret = -ENOMEM;