From b4c5242956bb7f68765dcc7c12e9560a8aa4d474 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 26 Feb 2019 23:40:09 -0800 Subject: arm64: dts: qcom: Add Dragonboard 845c This adds an initial dts for the Dragonboard 845. Supported functionality includes Debug UART, UFS, USB-C (peripheral), USB-A (host), Ethernet, microSD-card and Bluetooth. Initializing the SMMU is clearing the mapping used for the splash screen framebuffer, which causes the baord to reboot. This can be worked around using: fastboot oem select-display-panel none Ethernet and the second USB-A port depends on a functional firmware loader for the uPD720201. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/Makefile | 1 + arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 572 +++++++++++++++++++++++++++++ 2 files changed, 573 insertions(+) create mode 100644 arch/arm64/boot/dts/qcom/sdm845-db845c.dts diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 21d548f02d39..b3fe72ff2955 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -7,6 +7,7 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8992-bullhead-rev-101.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8994-angler-rev-101.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8996-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8998-mtp.dtb +dtb-$(CONFIG_ARCH_QCOM) += sdm845-db845c.dtb dtb-$(CONFIG_ARCH_QCOM) += sdm845-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-1000.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-4000.dtb diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts new file mode 100644 index 000000000000..c35be25cb876 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 + +/dts-v1/; + +#include +#include +#include +#include "sdm845.dtsi" +#include "pm8998.dtsi" +#include "pmi8998.dtsi" + +/ { + model = "Thundercomm Dragonboard 845c"; + compatible = "thundercomm,db845c", "qcom,sdm845"; + + aliases { + serial0 = &uart9; + hsuart0 = &uart6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + dc12v: dc12v-regulator { + compatible = "regulator-fixed"; + regulator-name = "DC12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + }; + + lt9611_1v8: lt9611-vdd18-regulator { + compatible = "regulator-fixed"; + regulator-name = "LT9611_1V8"; + + vin-supply = <&vdc_5v>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&tlmm 89 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + lt9611_3v3: lt9611-3v3 { + compatible = "regulator-fixed"; + regulator-name = "LT9611_3V3"; + + vin-supply = <&vdc_3v3>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + // TODO: make it possible to drive same GPIO from two clients + // gpio = <&tlmm 89 GPIO_ACTIVE_HIGH>; + // enable-active-high; + }; + + pcie0_1p05v: pcie-0-1p05v-regulator { + compatible = "regulator-fixed"; + regulator-name = "PCIE0_1.05V"; + + vin-supply = <&vbat>; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + + // TODO: make it possible to drive same GPIO from two clients + // gpio = <&tlmm 90 GPIO_ACTIVE_HIGH>; + // enable-active-high; + }; + + pcie0_3p3v_dual: vldo-3v3-regulator { + compatible = "regulator-fixed"; + regulator-name = "VLDO_3V3"; + + vin-supply = <&vbat>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 90 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + gpio_keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + autorepeat; + + pinctrl-names = "default"; + pinctrl-0 = <&vol_up_pin_a>; + + vol-up { + label = "Volume Up"; + linux,code = ; + gpios = <&pm8998_gpio 6 GPIO_ACTIVE_LOW>; + }; + }; + + leds { + compatible = "gpio-leds"; + + user4 { + label = "db845c:green:user4"; + gpios = <&pm8998_gpio 13 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + wlan { + label = "db845c:yellow:wlan"; + gpios = <&pm8998_gpio 9 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tx"; + default-state = "off"; + }; + + bt { + label = "db845c:blue:bt"; + gpios = <&pm8998_gpio 5 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "bluetooth-power"; + default-state = "off"; + }; + }; + + v5p0_hdmiout: v5p0-hdmiout-regulator { + compatible = "regulator-fixed"; + regulator-name = "V5P0_HDMIOUT"; + + vin-supply = <&vdc_5v>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <500000>; + + // TODO: make it possible to drive same GPIO from two clients + // gpio = <&tlmm 89 GPIO_ACTIVE_HIGH>; + // enable-active-high; + }; + + vbat: vbat-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT"; + + vin-supply = <&dc12v>; + regulator-min-microvolt = <4200000>; + regulator-max-microvolt = <4200000>; + regulator-always-on; + }; + + vbat_som: vbat-som-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT_SOM"; + + vin-supply = <&dc12v>; + regulator-min-microvolt = <4200000>; + regulator-max-microvolt = <4200000>; + regulator-always-on; + }; + + vdc_3v3: vdc-3v3-regulator { + compatible = "regulator-fixed"; + regulator-name = "VDC_3V3"; + vin-supply = <&dc12v>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vdc_5v: vdc-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "VDC_5V"; + + vin-supply = <&dc12v>; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <500000>; + regulator-always-on; + }; + + vreg_s4a_1p8: vreg-s4a-1p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_s4a_1p8"; + + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + + vin-supply = <&vbat_som>; + }; +}; + +&apps_rsc { + pm8998-rpmh-regulators { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + vdd-s7-supply = <&vph_pwr>; + vdd-s8-supply = <&vph_pwr>; + vdd-s9-supply = <&vph_pwr>; + vdd-s10-supply = <&vph_pwr>; + vdd-s11-supply = <&vph_pwr>; + vdd-s12-supply = <&vph_pwr>; + vdd-s13-supply = <&vph_pwr>; + vdd-l1-l27-supply = <&vreg_s7a_1p025>; + vdd-l2-l8-l17-supply = <&vreg_s3a_1p35>; + vdd-l3-l11-supply = <&vreg_s7a_1p025>; + vdd-l4-l5-supply = <&vreg_s7a_1p025>; + vdd-l6-supply = <&vph_pwr>; + vdd-l7-l12-l14-l15-supply = <&vreg_s5a_2p04>; + vdd-l9-supply = <&vreg_bob>; + vdd-l10-l23-l25-supply = <&vreg_bob>; + vdd-l13-l19-l21-supply = <&vreg_bob>; + vdd-l16-l28-supply = <&vreg_bob>; + vdd-l18-l22-supply = <&vreg_bob>; + vdd-l20-l24-supply = <&vreg_bob>; + vdd-l26-supply = <&vreg_s3a_1p35>; + vin-lvs-1-2-supply = <&vreg_s4a_1p8>; + + vreg_s3a_1p35: smps3 { + regulator-min-microvolt = <1352000>; + regulator-max-microvolt = <1352000>; + }; + + vreg_s5a_2p04: smps5 { + regulator-min-microvolt = <1904000>; + regulator-max-microvolt = <2040000>; + }; + + vreg_s7a_1p025: smps7 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1028000>; + }; + + vreg_l1a_0p875: ldo1 { + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l12a_1p8: ldo12 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l7a_1p8: ldo7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l13a_2p95: ldo13 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l17a_1p3: ldo17 { + regulator-min-microvolt = <1304000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l20a_2p95: ldo20 { + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2968000>; + regulator-initial-mode = ; + }; + + vreg_l21a_2p95: ldo21 { + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2968000>; + regulator-initial-mode = ; + }; + + vreg_l24a_3p075: ldo24 { + regulator-min-microvolt = <3088000>; + regulator-max-microvolt = <3088000>; + regulator-initial-mode = ; + }; + + vreg_l25a_3p3: ldo25 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3312000>; + regulator-initial-mode = ; + }; + + vreg_l26a_1p2: ldo26 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + pmi8998-rpmh-regulators { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-bob-supply = <&vph_pwr>; + + vreg_bob: bob { + regulator-min-microvolt = <3312000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = ; + regulator-allow-bypass; + }; + }; +}; + +&gcc { + protected-clocks = , + , + ; +}; + +&pcie0 { + status = "okay"; + perst-gpio = <&tlmm 35 GPIO_ACTIVE_LOW>; + enable-gpio = <&tlmm 134 GPIO_ACTIVE_HIGH>; + + vddpe-3v3-supply = <&pcie0_3p3v_dual>; + + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_perst_state>, <&pcie0_clkreq_state>, <&pcie0_wake_state>; + + vddpe-3v3-supply = <&pcie0_3p3v_dual>; +}; + +&pcie0_phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l26a_1p2>; +}; + +&pm8998_gpio { + vol_up_pin_a: vol-up-active { + pins = "gpio6"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; +}; + +&pm8998_pon { + resin { + compatible = "qcom,pm8941-resin"; + interrupts = <0x0 0x8 1 IRQ_TYPE_EDGE_BOTH>; + debounce = <15625>; + bias-pull-up; + linux,code = ; + }; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&sdhc_2 { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data &sd_card_det_n>; + + vmmc-supply = <&vreg_l21a_2p95>; + vqmmc-supply = <&vreg_l13a_2p95>; + + bus-width = <4>; + cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>; +}; + +&tlmm { + pcie0_pwren_state: pcie0-pwren { + pins = "gpio90"; + function = "gpio"; + + drive-strength = <2>; + bias-disable; + }; + + pcie0_perst_state: pcie0-reset { + pins = "gpio35"; + function = "gpio"; + + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + pcie0_clkreq_state: pcie0-clkreq { + pins = "gpio36"; + function = "pci_e0"; + bias-pull-up; + }; + + pcie0_wake_state: pcie0-wake { + pins = "gpio37"; + function = "gpio"; + + drive-strength = <2>; + bias-pull-up; + }; + + sdc2_clk: sdc2-clk { + pins = "sdc2_clk"; + bias-disable; + + /* + * It seems that mmc_test reports errors if drive + * strength is not 16 on clk, cmd, and data pins. + */ + drive-strength = <16>; + }; + + sdc2_cmd: sdc2-cmd { + pins = "sdc2_cmd"; + bias-pull-up; + drive-strength = <10>; + }; + + sdc2_data: sdc2-data { + pins = "sdc2_data"; + bias-pull-up; + drive-strength = <10>; + }; + + sd_card_det_n: sd-card-det-n { + pins = "gpio126"; + function = "gpio"; + bias-pull-up; + }; +}; + +&uart6 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn3990-bt"; + + vddio-supply = <&vreg_s4a_1p8>; + vddxo-supply = <&vreg_l7a_1p8>; + vddrf-supply = <&vreg_l17a_1p3>; + vddch0-supply = <&vreg_l25a_3p3>; + max-speed = <3200000>; + }; +}; + +&uart9 { + status = "okay"; +}; + +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3 { + dr_mode = "peripheral"; +}; + +&usb_1_hsphy { + status = "okay"; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l12a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; + + qcom,imp-res-offset-value = <8>; + qcom,hstx-trim-value = ; + qcom,preemphasis-level = ; + qcom,preemphasis-width = ; +}; + +&usb_1_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l26a_1p2>; + vdda-pll-supply = <&vreg_l1a_0p875>; +}; + +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3 { + dr_mode = "host"; +}; + +&usb_2_hsphy { + status = "okay"; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l12a_1p8>; + vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; + + qcom,imp-res-offset-value = <8>; + qcom,hstx-trim-value = ; +}; + +&usb_2_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l26a_1p2>; + vdda-pll-supply = <&vreg_l1a_0p875>; +}; + +&ufs_mem_hc { + status = "okay"; + + vcc-supply = <&vreg_l20a_2p95>; + vcc-max-microamp = <800000>; +}; + +&ufs_mem_phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l26a_1p2>; +}; + +&wifi { + status = "okay"; +}; + +/* PINCTRL - additions to nodes defined in sdm845.dtsi */ + +&qup_uart6_default { + pinmux { + pins = "gpio45", "gpio46", "gpio47", "gpio48"; + function = "qup6"; + }; + + cts { + pins = "gpio45"; + bias-disable; + }; + + rts-tx { + pins = "gpio46", "gpio47"; + drive-strength = <2>; + bias-disable; + }; + + rx { + pins = "gpio48"; + bias-pull-up; + }; +}; + +&qup_uart9_default { + pinconf-tx { + pins = "gpio4"; + drive-strength = <2>; + bias-disable; + }; + + pinconf-rx { + pins = "gpio5"; + drive-strength = <2>; + bias-pull-up; + }; +}; -- cgit v1.2.3 From 7c2b228f181d5578c1cef625ca10f5e08c5d1175 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 14 Feb 2019 23:02:06 -0800 Subject: drm/bridge: Introduce LT9611 DSI to HDMI bridge Signed-off-by: Bjorn Andersson --- drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/lt9611.c | 1140 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1149 insertions(+) create mode 100644 drivers/gpu/drm/bridge/lt9611.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 8840f396a7b6..994bda728e80 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -44,6 +44,14 @@ config DRM_DUMB_VGA_DAC Support for non-programmable RGB to VGA DAC bridges, such as ADI ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs. +config DRM_LONTIUM_LT9611 + tristate "Lontium LT9611 DSI/HDMI bridge" + depends on OF + select DRM_PANEL_BRIDGE + select DRM_KMS_HELPER + help + Lontium LT9611 DSI/HDMI bridge chip driver. + config DRM_LVDS_ENCODER tristate "Transparent parallel to LVDS encoder support" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 4934fcf5a6f8..8b1d176da6a2 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o +obj-$(CONFIG_DRM_LONTIUM_LT9611) += lt9611.o obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o diff --git a/drivers/gpu/drm/bridge/lt9611.c b/drivers/gpu/drm/bridge/lt9611.c new file mode 100644 index 000000000000..c7be5f650629 --- /dev/null +++ b/drivers/gpu/drm/bridge/lt9611.c @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2019. Linaro Ltd + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EDID_SEG_SIZE 256 + +#define LT9611_4LANES 0 + +struct lt9611 { + struct device *dev; + struct drm_bridge bridge; + struct drm_connector connector; + + struct regmap *regmap; + + struct device_node *dsi0_node; + struct device_node *dsi1_node; + struct mipi_dsi_device *dsi0; + struct mipi_dsi_device *dsi1; + + bool ac_mode; + + struct gpio_desc *reset_gpio; + struct gpio_desc *enable_gpio; + + bool power_on; + + struct regulator_bulk_data supplies[2]; + + struct i2c_client *client; + + enum drm_connector_status status; + + u8 edid_buf[EDID_SEG_SIZE]; + u32 vic; +}; + +static const struct regmap_config lt9611_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +struct lt9611_mode { + u16 hdisplay; + u16 vdisplay; + u8 fps; + u8 lanes; + u8 intfs; +}; + +static struct lt9611_mode lt9611_modes[] = { + { 3840, 2160, 30, 4, 2 }, /* 3840x2160 24bit 30Hz 4Lane 2ports */ + { 1920, 1080, 60, 4, 1 }, /* 1080P 24bit 60Hz 4lane 1port */ + { 1920, 1080, 30, 3, 1 }, /* 1080P 24bit 30Hz 3lane 1port */ + { 1920, 1080, 24, 3, 1 }, + { 720, 480, 60, 4, 1 }, + { 720, 576, 50, 2, 1 }, + { 640, 480, 60, 2, 1 }, +}; + +static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge) +{ + return container_of(bridge, struct lt9611, bridge); +} + +static struct lt9611 *connector_to_lt9611(struct drm_connector *connector) +{ + return container_of(connector, struct lt9611, connector); +} + +static int lt9611_mipi_input_analog(struct lt9611 *lt9611) +{ + struct reg_sequence reg_cfg[] = { + { 0xff, 0x81 }, + { 0x06, 0x40 }, /*port A rx current*/ + { 0x0a, 0xfe }, /*port A ldo voltage set*/ + { 0x0b, 0xbf }, /*enable port A lprx*/ + { 0x11, 0x40 }, /*port B rx current*/ + { 0x15, 0xfe }, /*port B ldo voltage set*/ + { 0x16, 0xbf }, /*enable port B lprx*/ + + { 0x1c, 0x03 }, /*PortA clk lane no-LP mode*/ + { 0x20, 0x03 }, /*PortB clk lane with-LP mode*/ + }; + + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + + return 0; +} + +static int lt9611_mipi_input_digital(struct lt9611 *lt9611, + const struct drm_display_mode *mode) +{ + struct reg_sequence reg_cfg[] = { + { 0xff, 0x82 }, + { 0x4f, 0x80 }, + { 0x50, 0x10 }, + { 0xff, 0x83 }, + + { 0x02, 0x0a }, + { 0x06, 0x0a }, + }; + + regmap_write(lt9611->regmap, 0xff, 0x83); + regmap_write(lt9611->regmap, 0x00, LT9611_4LANES); + + if (mode->hdisplay == 3840) + regmap_write(lt9611->regmap, 0x0a, 0x03); + else + regmap_write(lt9611->regmap, 0x0a, 0x00); + + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + + return 0; +} + +static void lt9611_mipi_video_setup(struct lt9611 *lt9611, + const struct drm_display_mode *mode) +{ + u32 h_total, h_act, hpw, hfp, hss; + u32 v_total, v_act, vpw, vfp, vss; + + h_total = mode->htotal; + v_total = mode->vtotal; + + h_act = mode->hdisplay; + hpw = mode->hsync_end - mode->hsync_start; + hfp = mode->hsync_start - mode->hdisplay; + hss = (mode->hsync_end - mode->hsync_start) + (mode->htotal - mode->hsync_end); + + v_act = mode->vdisplay; + vpw = mode->vsync_end - mode->vsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vss = (mode->vsync_end - mode->vsync_start) + (mode->vtotal - mode->vsync_end); + + regmap_write(lt9611->regmap, 0xff, 0x83); + + regmap_write(lt9611->regmap, 0x0d, (u8)(v_total / 256)); + regmap_write(lt9611->regmap, 0x0e, (u8)(v_total % 256)); + + regmap_write(lt9611->regmap, 0x0f, (u8)(v_act / 256)); + regmap_write(lt9611->regmap, 0x10, (u8)(v_act % 256)); + + regmap_write(lt9611->regmap, 0x11, (u8)(h_total / 256)); + regmap_write(lt9611->regmap, 0x12, (u8)(h_total % 256)); + + regmap_write(lt9611->regmap, 0x13, (u8)(h_act / 256)); + regmap_write(lt9611->regmap, 0x14, (u8)(h_act % 256)); + + regmap_write(lt9611->regmap, 0x15, (u8)(vpw % 256)); + regmap_write(lt9611->regmap, 0x16, (u8)(hpw % 256)); + + regmap_write(lt9611->regmap, 0x17, (u8)(vfp % 256)); + + regmap_write(lt9611->regmap, 0x18, (u8)(vss % 256)); + + regmap_write(lt9611->regmap, 0x19, (u8)(hfp % 256)); + + regmap_write(lt9611->regmap, 0x1a, (u8)(hss / 256)); + regmap_write(lt9611->regmap, 0x1b, (u8)(hss % 256)); +} + +static int lt9611_pcr_setup(struct lt9611 *lt9611, + const struct drm_display_mode *mode) +{ + struct reg_sequence reg_cfg[] = { + { 0xff, 0x83 }, + { 0x0b, 0x01 }, + { 0x0c, 0x10 }, + { 0x48, 0x00 }, + { 0x49, 0x81 }, + + /* stage 1 */ + { 0x21, 0x4a }, + { 0x24, 0x71 }, + { 0x25, 0x30 }, + { 0x2a, 0x01 }, + + /* stage 2 */ + { 0x4a, 0x40 }, + { 0x1d, 0x10 }, + + /* MK limit */ + { 0x2d, 0x38 }, + { 0x31, 0x08 }, + }; + + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + + switch (mode->hdisplay) { + case 640: + regmap_write(lt9611->regmap, 0x26, 0x14); + break; + case 1920: + regmap_write(lt9611->regmap, 0x26, 0x37); + break; + case 3840: + regmap_write(lt9611->regmap, 0x0b, 0x03); + regmap_write(lt9611->regmap, 0x0c, 0xd0); + regmap_write(lt9611->regmap, 0x48, 0x03); + regmap_write(lt9611->regmap, 0x49, 0xe0); + regmap_write(lt9611->regmap, 0x24, 0x72); + regmap_write(lt9611->regmap, 0x25, 0x00); + regmap_write(lt9611->regmap, 0x2a, 0x01); + regmap_write(lt9611->regmap, 0x4a, 0x10); + regmap_write(lt9611->regmap, 0x1d, 0x10); + regmap_write(lt9611->regmap, 0x26, 0x37); + break; + } + + /* pcr rst */ + regmap_write(lt9611->regmap, 0xff, 0x80); + regmap_write(lt9611->regmap, 0x11, 0x5a); + regmap_write(lt9611->regmap, 0x11, 0xfa); + + return 0; +} + +static int lt9611_pll_setup(struct lt9611 *lt9611, + const struct drm_display_mode *mode) +{ + unsigned int pclk = mode->clock; + struct reg_sequence reg_cfg[] = { + /* txpll init */ + { 0xff, 0x81 }, + { 0x23, 0x40 }, + { 0x24, 0x64 }, + { 0x25, 0x80 }, + { 0x26, 0x55 }, + { 0x2c, 0x37 }, + { 0x2f, 0x01 }, + { 0x26, 0x55 }, + { 0x27, 0x66 }, + { 0x28, 0x88 }, + }; + + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); + + if (pclk > 150000) + regmap_write(lt9611->regmap, 0x2d, 0x88); + else if (pclk > 70000) + regmap_write(lt9611->regmap, 0x2d, 0x99); + else + regmap_write(lt9611->regmap, 0x2d, 0xaa); + + regmap_write(lt9611->regmap, 0xff, 0x82); + regmap_write(lt9611->regmap, 0xe3, pclk >> 17); /* pclk[19:16] */ + regmap_write(lt9611->regmap, 0xe4, pclk >> 9); /* pclk[15:8] */ + regmap_write(lt9611->regmap, 0xe5, pclk >> 1); /* pclk[7:0] */ + + regmap_write(lt9611->regmap, 0xde, 0x20); + regmap_write(lt9611->regmap, 0xde, 0xe0); + + regmap_write(lt9611->regmap, 0xff, 0x80); + regmap_write(lt9611->regmap, 0x16, 0xf1); + regmap_write(lt9611->regmap, 0x16, 0xf3); + + return 0; +} + +static int lt9611_video_check(struct lt9611 *lt9611) +{ + u32 v_total, v_act, h_act_a, h_act_b, h_total_sysclk; + unsigned int temp; + int ret; + + /* top module video check */ + regmap_write(lt9611->regmap, 0xff, 0x82); + + /* v_act */ + ret = regmap_read(lt9611->regmap, 0x82, &temp); + if (ret) + goto end; + + v_act = temp << 8; + ret = regmap_read(lt9611->regmap, 0x83, &temp); + if (ret) + goto end; + v_act = v_act + temp; + + /* v_total */ + ret = regmap_read(lt9611->regmap, 0x6c, &temp); + if (ret) + goto end; + v_total = temp << 8; + ret = regmap_read(lt9611->regmap, 0x6d, &temp); + if (ret) + goto end; + v_total = v_total + temp; + + /* h_total_sysclk */ + ret = regmap_read(lt9611->regmap, 0x86, &temp); + if (ret) + goto end; + h_total_sysclk = temp << 8; + ret = regmap_read(lt9611->regmap, 0x87, &temp); + if (ret) + goto end; + h_total_sysclk = h_total_sysclk + temp; + + /* h_act_a */ + regmap_write(lt9611->regmap, 0xff, 0x83); + ret = regmap_read(lt9611->regmap, 0x82, &temp); + if (ret) + goto end; + h_act_a = temp << 8; + ret = regmap_read(lt9611->regmap, 0x83, &temp); + if (ret) + goto end; + h_act_a = (h_act_a + temp)/3; + + /* h_act_b */ + regmap_write(lt9611->regmap, 0xff, 0x83); + ret = regmap_read(lt9611->regmap, 0x86, &temp); + if (ret) + goto end; + h_act_b = temp << 8; + ret = regmap_read(lt9611->regmap, 0x87, &temp); + if (ret) + goto end; + h_act_b = (h_act_b + temp)/3; + + dev_info(lt9611->dev, "video check: h_act_a=%d, h_act_b=%d, v_act=%d, v_total=%d, h_total_sysclk=%d\n", + h_act_a, h_act_b, v_act, v_total, h_total_sysclk); + + return 0; + +end: + dev_err(lt9611->dev, "read video check error\n"); + return ret; +} + +static void lt9611_hdmi_tx_digital(struct lt9611 *lt9611) +{ + regmap_write(lt9611->regmap, 0xff, 0x84); + regmap_write(lt9611->regmap, 0x43, 0x46 - lt9611->vic); + regmap_write(lt9611->regmap, 0x44, 0x84); + regmap_write(lt9611->regmap, 0x47, lt9611->vic); + + regmap_write(lt9611->regmap, 0xff, 0x82); + regmap_write(lt9611->regmap, 0xd6, 0x8c); + regmap_write(lt9611->regmap, 0xd7, 0x04); +} + +static void lt9611_hdmi_tx_phy(struct lt9611 *lt9611) +{ + struct reg_sequence reg_cfg[] = { + { 0xff, 0x81 }, + { 0x30, 0x6a }, + { 0x31, 0x44 }, /* HDMI DC mode */ + { 0x32, 0x4a }, + { 0x33, 0x0b }, + { 0x34, 0x00 }, + { 0x35, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x44 }, + { 0x3f, 0x0f }, + { 0x40, 0xa0 }, + { 0x41, 0xa0 }, + { 0x42, 0xa0 }, + { 0x43, 0xa0 }, + { 0x44, 0x0a }, + }; + + /* HDMI AC mode */ + if (lt9611->ac_mode) + reg_cfg[2].def = 0x73; + + regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); +} + +static irqreturn_t lt9611_irq_thread_handler(int irq, void *dev_id) +{ + struct lt9611 *lt9611 = dev_id; + unsigned int irq_flag0 = 0; + unsigned int irq_flag3 = 0; + + regmap_write(lt9611->regmap, 0xff, 0x82); + regmap_read(lt9611->regmap, 0x0f, &irq_flag3); + regmap_read(lt9611->regmap, 0x0c, &irq_flag0); + + printk(KERN_ERR "%s() irq_flag0: %#x irq_flag3: %#x\n", __func__, irq_flag0, irq_flag3); + + /* hpd changed low */ + if (irq_flag3 & 0x80) { + dev_info(lt9611->dev, "hdmi cable disconnected\n"); + + regmap_write(lt9611->regmap, 0xff, 0x82); /* irq 3 clear flag */ + regmap_write(lt9611->regmap, 0x07, 0xbf); + regmap_write(lt9611->regmap, 0x07, 0x3f); + } + /* hpd changed high */ + if (irq_flag3 & 0x40) { + dev_info(lt9611->dev, "hdmi cable connected\n"); + + regmap_write(lt9611->regmap, 0xff, 0x82); /* irq 3 clear flag */ + regmap_write(lt9611->regmap, 0x07, 0x7f); + regmap_write(lt9611->regmap, 0x07, 0x3f); + } + +// if (irq_flag3 & 0xc0) +// drm_kms_helper_hotplug_event(lt9611->bridge.dev); + + /* video input changed */ + if (irq_flag0 & 0x01) { + dev_info(lt9611->dev, "video input changed\n"); + regmap_write(lt9611->regmap, 0xff, 0x82); /* irq 0 clear flag */ + regmap_write(lt9611->regmap, 0x9e, 0xff); + regmap_write(lt9611->regmap, 0x9e, 0xf7); + regmap_write(lt9611->regmap, 0x04, 0xff); + regmap_write(lt9611->regmap, 0x04, 0xfe); + } + + return IRQ_HANDLED; +} + +static void lt9611_enable_hpd_interrupts(struct lt9611 *lt9611) +{ + unsigned int val; + + dev_dbg(lt9611->dev, "enabling hpd interrupts\n"); + + regmap_write(lt9611->regmap, 0xff, 0x82); + regmap_read(lt9611->regmap, 0x03, &val); + + val &= ~0xc0; + regmap_write(lt9611->regmap, 0x03, val); + regmap_write(lt9611->regmap, 0x07, 0xff); //clear + regmap_write(lt9611->regmap, 0x07, 0x3f); +} + +static void lt9611_sleep_setup(struct lt9611 *lt9611) +{ + struct reg_sequence sleep_setup[] = { + { 0xff, 0x80 }, //register I2C addr + { 0x24, 0x76 }, + { 0x23, 0x01 }, + { 0xff, 0x81 }, //set addr pin as output + { 0x57, 0x03 }, + { 0x49, 0x0b }, + { 0xff, 0x81 }, //anlog power down + { 0x51, 0x30 }, //disable IRQ + { 0x02, 0x48 }, //MIPI Rx power down + { 0x23, 0x80 }, + { 0x30, 0x00 }, + { 0x00, 0x01 }, //bandgap power down + { 0x01, 0x00 }, //system clk power down + }; + + dev_err(lt9611->dev, "sleep\n"); + + regmap_multi_reg_write(lt9611->regmap, sleep_setup, ARRAY_SIZE(sleep_setup)); +} + +static int lt9611_power_on(struct lt9611 *lt9611) +{ + int ret; + const struct reg_sequence seq[] = { + /* LT9611_System_Init */ + { 0xFF, 0x81 }, + { 0x01, 0x18 }, /* sel xtal clock */ + + /* timer for frequency meter */ + { 0xff, 0x82 }, + { 0x1b, 0x69 }, /*timer 2*/ + { 0x1c, 0x78 }, + { 0xcb, 0x69 }, /*timer 1 */ + { 0xcc, 0x78 }, + + /* irq init */ + { 0xff, 0x82 }, + { 0x51, 0x01 }, + { 0x58, 0x0a }, /* hpd irq */ + { 0x59, 0x80 }, /* hpd debounce width */ + { 0x9e, 0xf7 }, /* video check irq */ + + /* power consumption for work */ + { 0xff, 0x80 }, + { 0x04, 0xf0 }, + { 0x06, 0xf0 }, + { 0x0a, 0x80 }, + { 0x0b, 0x40 }, + { 0x0d, 0xef }, + { 0x11, 0xfa }, + }; + + if (lt9611->power_on) + return 0; + + dev_dbg(lt9611->dev, "power on\n"); + + ret = regmap_multi_reg_write(lt9611->regmap, seq, ARRAY_SIZE(seq)); + if (!ret) + lt9611->power_on = true; + + return ret; +} + +static int lt9611_power_off(struct lt9611 *lt9611) +{ + int ret; + const struct reg_sequence off[] = { + { 0xff, 0x81 }, + { 0x30, 0x6a }, + }; + + dev_dbg(lt9611->dev, "power off\n"); + + ret = regmap_multi_reg_write(lt9611->regmap, off, ARRAY_SIZE(off)); + if (!ret) + lt9611->power_on = false; + + return ret; +} + +static void lt9611_reset(struct lt9611 *lt9611) +{ + gpiod_set_value_cansleep(lt9611->reset_gpio, 1); + msleep(20); + gpiod_set_value_cansleep(lt9611->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(lt9611->reset_gpio, 1); + msleep(100); +} + +static void lt9611_assert_5v(struct lt9611 *lt9611) +{ + if (!lt9611->enable_gpio) + return; + + gpiod_set_value_cansleep(lt9611->enable_gpio, 1); + msleep(20); +} + +static int lt9611_regulator_init(struct lt9611 *lt9611) +{ + int ret; + + lt9611->supplies[0].supply = "vdd"; + lt9611->supplies[1].supply = "vcc"; + ret = devm_regulator_bulk_get(lt9611->dev, 2, lt9611->supplies); + if (ret < 0) + return ret; + + return regulator_set_load(lt9611->supplies[0].consumer, 300000); +} + +static int lt9611_regulator_enable(struct lt9611 *lt9611) +{ + int ret; + + ret = regulator_enable(lt9611->supplies[0].consumer); + if (ret < 0) + return ret; + + usleep_range(1000, 10000); + + ret = regulator_enable(lt9611->supplies[1].consumer); + if (ret < 0) { + regulator_disable(lt9611->supplies[0].consumer); + return ret; + } + + return 0; +} + +static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lt9611_modes); i++) { + if (lt9611_modes[i].hdisplay == mode->hdisplay && + lt9611_modes[i].vdisplay == mode->vdisplay && + lt9611_modes[i].fps == drm_mode_vrefresh(mode)) { + return <9611_modes[i]; + } + } + + return NULL; +} + +/* connector funcs */ +static enum drm_connector_status +lt9611_connector_detect(struct drm_connector *connector, bool force) +{ + struct lt9611 *lt9611 = connector_to_lt9611(connector); + unsigned int reg_val = 0; + int connected = 0; + + regmap_write(lt9611->regmap, 0xff, 0x82); + regmap_read(lt9611->regmap, 0x5e, ®_val); + connected = (reg_val & BIT(2)); + dev_dbg(lt9611->dev, "connected = %x\n", connected); + + lt9611->status = connected ? connector_status_connected : + connector_status_disconnected; + + return lt9611->status; +} + +static int lt9611_read_edid(struct lt9611 *lt9611) +{ + unsigned int temp; + int ret = 0; + int i, j; + + memset(lt9611->edid_buf, 0, EDID_SEG_SIZE); + + regmap_write(lt9611->regmap, 0xff, 0x85); + regmap_write(lt9611->regmap, 0x03, 0xc9); + regmap_write(lt9611->regmap, 0x04, 0xa0); /* 0xA0 is EDID device address */ + regmap_write(lt9611->regmap, 0x05, 0x00); /* 0x00 is EDID offset address */ + regmap_write(lt9611->regmap, 0x06, 0x20); /* length for read */ + regmap_write(lt9611->regmap, 0x14, 0x7f); + + for (i = 0 ; i < 8 ; i++) { + regmap_write(lt9611->regmap, 0x05, i * 32); /* offset address */ + regmap_write(lt9611->regmap, 0x07, 0x36); + regmap_write(lt9611->regmap, 0x07, 0x31); + regmap_write(lt9611->regmap, 0x07, 0x37); + usleep_range(5000, 10000); + + regmap_read(lt9611->regmap, 0x40, &temp); + + if (temp & 0x02) { /*KEY_DDC_ACCS_DONE=1*/ + for (j = 0; j < 32; j++) { + regmap_read(lt9611->regmap, 0x83, &temp); + lt9611->edid_buf[i*32+j] = temp; + } + } else if (temp & 0x50) { /* DDC No Ack or Abitration lost */ + dev_err(lt9611->dev, "read edid failed: no ack\n"); + ret = -EIO; + goto end; + } else { + dev_err(lt9611->dev, "read edid failed: access not done\n"); + ret = -EIO; + goto end; + } + } + + dev_dbg(lt9611->dev, "read edid succeeded, checksum = 0x%x\n", + lt9611->edid_buf[255]); + +end: + regmap_write(lt9611->regmap, 0x07, 0x1f); + return ret; +} + +/* TODO: add support for more extenstion blocks */ +static int lt9611_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + struct lt9611 *lt9611 = data; + int ret; + + dev_dbg(lt9611->dev, "get edid block: block=%d, len=%d\n", block, (int)len); + + if (len > 128) + return -EINVAL; + + /* support up to 1 extension block */ + if (block > 1) + return -EINVAL; + + if (block == 0) { + /* always read 2 edid blocks once */ + ret = lt9611_read_edid(lt9611); + if (ret) { + dev_err(lt9611->dev, "edid read failed\n"); + return ret; + } + } + + if (block % 2 == 0) + memcpy(buf, lt9611->edid_buf, len); + else + memcpy(buf, lt9611->edid_buf + 128, len); + + return 0; +} + +static int lt9611_connector_get_modes(struct drm_connector *connector) +{ + struct lt9611 *lt9611 = connector_to_lt9611(connector); + unsigned int count; + struct edid *edid; + + dev_dbg(lt9611->dev, "get modes\n"); + + lt9611_power_on(lt9611); + edid = drm_do_get_edid(connector, lt9611_get_edid_block, lt9611); + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + kfree(edid); + + return count; +} + +static enum drm_mode_status lt9611_connector_mode_valid( + struct drm_connector *connector, struct drm_display_mode *mode) +{ + struct lt9611_mode *lt9611_mode = lt9611_find_mode(mode); + + return lt9611_mode ? MODE_OK : MODE_BAD; +} + +/* bridge funcs */ +static void lt9611_bridge_enable(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + const struct reg_sequence on[] = { + { 0xff, 0x81 }, + { 0x30, 0xea }, + }; + + dev_dbg(lt9611->dev, "bridge enable\n"); + + if (lt9611_power_on(lt9611)) { + dev_err(lt9611->dev, "power on failed\n"); + return; + } + + dev_dbg(lt9611->dev, "video on\n"); + + lt9611_mipi_input_analog(lt9611); + lt9611_hdmi_tx_digital(lt9611); + lt9611_hdmi_tx_phy(lt9611); + + msleep(500); + + lt9611_video_check(lt9611); + + /* Enable HDMI output */ + regmap_multi_reg_write(lt9611->regmap, on, ARRAY_SIZE(on)); +} + +static void lt9611_bridge_disable(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + int ret; + const struct reg_sequence hdmi_off[] = { + { 0xff, 0x81 }, + { 0x30, 0x6a }, + }; + + dev_dbg(lt9611->dev, "bridge disable\n"); + + /* Disable HDMI output */ + ret = regmap_multi_reg_write(lt9611->regmap, hdmi_off, ARRAY_SIZE(hdmi_off)); + if (ret) { + dev_err(lt9611->dev, "video on failed\n"); + return; + } + + if (lt9611_power_off(lt9611)) { + dev_err(lt9611->dev, "power on failed\n"); + return; + } +} + +static struct drm_connector_helper_funcs lt9611_bridge_connector_helper_funcs = { + .get_modes = lt9611_connector_get_modes, + .mode_valid = lt9611_connector_mode_valid, +}; + +static const struct drm_connector_funcs lt9611_bridge_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = lt9611_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611, + struct device_node *dsi_node) +{ + const struct mipi_dsi_device_info info = { "lt9611", 0, NULL }; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + int ret; + + host = of_find_mipi_dsi_host_by_node(dsi_node); + if (!host) { + dev_err(lt9611->dev, "failed to find dsi host\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + dev_err(lt9611->dev, "failed to create dsi device\n"); + return dsi; + } + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_VIDEO_HSE; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(lt9611->dev, "failed to attach dsi to host\n"); + mipi_dsi_device_unregister(dsi); + return ERR_PTR(ret); + } + + return dsi; +} + +static int lt9611_bridge_attach(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + int ret; + + dev_dbg(lt9611->dev, "bridge attach\n"); + + ret = drm_connector_init(bridge->dev, <9611->connector, + <9611_bridge_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + drm_connector_helper_add(<9611->connector, + <9611_bridge_connector_helper_funcs); + drm_connector_attach_encoder(<9611->connector, bridge->encoder); + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + /* Attach primary DSI */ + lt9611->dsi0 = lt9611_attach_dsi(lt9611, lt9611->dsi0_node); + if (IS_ERR(lt9611->dsi0)) + return PTR_ERR(lt9611->dsi0); + + /* Attach secondary DSI, if specified */ + if (lt9611->dsi1_node) { + lt9611->dsi1 = lt9611_attach_dsi(lt9611, lt9611->dsi1_node); + if (IS_ERR(lt9611->dsi1)) { + ret = PTR_ERR(lt9611->dsi1); + goto err_unregister_dsi0; + } + } + + return 0; + +err_unregister_dsi0: + mipi_dsi_device_unregister(lt9611->dsi0); + + return ret; +} + +static void lt9611_bridge_detach(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + + if (lt9611->dsi1) { + mipi_dsi_detach(lt9611->dsi1); + mipi_dsi_device_unregister(lt9611->dsi1); + } + + mipi_dsi_detach(lt9611->dsi0); + mipi_dsi_device_unregister(lt9611->dsi0); +} + +static enum drm_mode_status +lt9611_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + struct lt9611_mode *lt9611_mode = lt9611_find_mode(mode); + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + + if (lt9611_mode->intfs > 1 && !lt9611->dsi1) + return MODE_PANEL; + else + return MODE_OK; +} + +static void lt9611_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + + dev_dbg(lt9611->dev, "bridge pre_enable\n"); + + regmap_write(lt9611->regmap, 0xff, 0x80); + regmap_write(lt9611->regmap, 0xee, 0x01); +} + +static void lt9611_bridge_post_disable(struct drm_bridge *bridge) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + + dev_dbg(lt9611->dev, "bridge post_disable\n"); + + lt9611_sleep_setup(lt9611); +} + +static void lt9611_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adj_mode) +{ + struct lt9611 *lt9611 = bridge_to_lt9611(bridge); + struct hdmi_avi_infoframe avi_frame; + int ret; + + dev_dbg(lt9611->dev, "bridge mode_set: hdisplay=%d, vdisplay=%d, vrefresh=%d, clock=%d\n", + adj_mode->hdisplay, adj_mode->vdisplay, + adj_mode->vrefresh, adj_mode->clock); + + lt9611_mipi_input_digital(lt9611, mode); + lt9611_pll_setup(lt9611, mode); + lt9611_mipi_video_setup(lt9611, mode); + lt9611_pcr_setup(lt9611, mode); + + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, + <9611->connector, + mode); + if (!ret) + lt9611->vic = avi_frame.video_code; +} + +static const struct drm_bridge_funcs lt9611_bridge_funcs = { + .attach = lt9611_bridge_attach, + .detach = lt9611_bridge_detach, + .mode_valid = lt9611_bridge_mode_valid, + .pre_enable = lt9611_bridge_pre_enable, + .enable = lt9611_bridge_enable, + .disable = lt9611_bridge_disable, + .post_disable = lt9611_bridge_post_disable, + .mode_set = lt9611_bridge_mode_set, +}; + +static int lt9611_parse_dt(struct device *dev, + struct lt9611 *lt9611) +{ + lt9611->dsi0_node = of_graph_get_remote_node(dev->of_node, 1, -1); + if (!lt9611->dsi0_node) { + DRM_DEV_ERROR(dev, + "failed to get remote node for primary dsi\n"); + return -ENODEV; + } + + lt9611->dsi1_node = of_graph_get_remote_node(dev->of_node, 2, -1); + + lt9611->ac_mode = of_property_read_bool(dev->of_node, "lt,ac-mode"); + dev_dbg(lt9611->dev, "ac_mode=%d\n", lt9611->ac_mode); + + return 0; +} + +static int lt9611_gpio_init(struct lt9611 *lt9611) +{ + struct device *dev = lt9611->dev; + + lt9611->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(lt9611->reset_gpio)) { + dev_err(dev, "failed to acquire reset gpio\n"); + return PTR_ERR(lt9611->reset_gpio); + } + + lt9611->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(lt9611->enable_gpio)) { + dev_err(dev, "failed to acquire enable gpio\n"); + return PTR_ERR(lt9611->enable_gpio); + } + + return 0; +} + +static int lt9611_read_device_rev(struct lt9611 *lt9611) +{ + unsigned int rev; + int ret; + + regmap_write(lt9611->regmap, 0xff, 0x80); + regmap_write(lt9611->regmap, 0xee, 0x01); + + ret = regmap_read(lt9611->regmap, 0x02, &rev); + if (ret) + dev_err(lt9611->dev, "failed to read revision: %d\n", ret); + + dev_info(lt9611->dev, "LT9611 revsion: 0x%x\n", rev); + + return ret; +} + +static int lt9611_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lt9611 *lt9611; + struct device *dev = &client->dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "device doesn't support I2C\n"); + return -ENODEV; + } + + lt9611 = devm_kzalloc(dev, sizeof(*lt9611), GFP_KERNEL); + if (!lt9611) + return -ENOMEM; + + lt9611->dev = &client->dev; + lt9611->client = client; + + lt9611->regmap = devm_regmap_init_i2c(client, <9611_regmap_config); + if (IS_ERR(lt9611->regmap)) { + DRM_ERROR("regmap i2c init failed\n"); + return PTR_ERR(lt9611->regmap); + } + + ret = lt9611_parse_dt(&client->dev, lt9611); + if (ret) { + dev_err(dev, "failed to parse device tree\n"); + return ret; + } + + ret = lt9611_gpio_init(lt9611); + if (ret < 0) + return ret; + + ret = lt9611_regulator_init(lt9611); + if (ret < 0) + return ret; + + lt9611_assert_5v(lt9611); + + ret = lt9611_regulator_enable(lt9611); + if (ret) + return ret; + + lt9611_reset(lt9611); + + ret = lt9611_read_device_rev(lt9611); + if (ret) { + dev_err(dev, "failed to read chip rev\n"); + goto err_disable_regulators; + } + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + lt9611_irq_thread_handler, + IRQF_ONESHOT, "lt9611", lt9611); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto err_disable_regulators; + } + + i2c_set_clientdata(client, lt9611); + + lt9611->bridge.funcs = <9611_bridge_funcs; + lt9611->bridge.of_node = client->dev.of_node; + + drm_bridge_add(<9611->bridge); + + lt9611_enable_hpd_interrupts(lt9611); + + return 0; + +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies); + + of_node_put(lt9611->dsi0_node); + of_node_put(lt9611->dsi1_node); + + return ret; +} + +static int lt9611_remove(struct i2c_client *client) +{ + struct lt9611 *lt9611 = i2c_get_clientdata(client); + + disable_irq(client->irq); + + drm_bridge_remove(<9611->bridge); + + regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies); + + of_node_put(lt9611->dsi0_node); + of_node_put(lt9611->dsi1_node); + + return 0; +} + + +static struct i2c_device_id lt9611_id[] = { + { "lt,lt9611", 0}, + {} +}; + +static const struct of_device_id lt9611_match_table[] = { + {.compatible = "lt,lt9611"}, + {} +}; +MODULE_DEVICE_TABLE(of, lt9611_match_table); + +static struct i2c_driver lt9611_driver = { + .driver = { + .name = "lt9611", + .of_match_table = lt9611_match_table, + }, + .probe = lt9611_probe, + .remove = lt9611_remove, + .id_table = lt9611_id, +}; +module_i2c_driver(lt9611_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e64b2c435661e186ddbf9618818ce05bd070c1b4 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 7 Mar 2019 21:44:37 -0800 Subject: drm/bridge: lt9611: Add i2s initialization Signed-off-by: Bjorn Andersson --- drivers/gpu/drm/bridge/lt9611.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/bridge/lt9611.c b/drivers/gpu/drm/bridge/lt9611.c index c7be5f650629..348ec76a3a3d 100644 --- a/drivers/gpu/drm/bridge/lt9611.c +++ b/drivers/gpu/drm/bridge/lt9611.c @@ -543,6 +543,23 @@ static int lt9611_power_off(struct lt9611 *lt9611) return ret; } +static void lt9611_i2s_init(struct lt9611 *lt9611) +{ + const struct reg_sequence init[] = { + { 0xff, 0x82 }, + { 0xd6, 0x8c }, + { 0xd7, 0x04 }, + + { 0xff, 0x84 }, + { 0x06, 0x08 }, + { 0x07, 0x10 }, + + { 0x34, 0xd4 }, + }; + + regmap_multi_reg_write(lt9611->regmap, init, ARRAY_SIZE(init)); +} + static void lt9611_reset(struct lt9611 *lt9611) { gpiod_set_value_cansleep(lt9611->reset_gpio, 1); @@ -1070,6 +1087,8 @@ static int lt9611_probe(struct i2c_client *client, goto err_disable_regulators; } + lt9611_i2s_init(lt9611); + ret = devm_request_threaded_irq(dev, client->irq, NULL, lt9611_irq_thread_handler, IRQF_ONESHOT, "lt9611", lt9611); -- cgit v1.2.3 From 855a10b0afd0b19dd166780cd97b06e2a79287c4 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 14 Feb 2019 11:45:50 -0800 Subject: arm64: dts: qcom: sdm845-db845c: Bring in LT9611 Enable MDSS and DSI and add the LT9611 HDMI bridge. DSI1 is supposedly needed for 4k support, but is left commented out as this is yet to be functional. Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 145 +++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts index c35be25cb876..3ff12933b282 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts @@ -30,6 +30,17 @@ regulator-always-on; }; + hdmi-out { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <<9611_out>; + }; + }; + }; + lt9611_1v8: lt9611-vdd18-regulator { compatible = "regulator-fixed"; regulator-name = "LT9611_1V8"; @@ -313,12 +324,123 @@ }; }; +&cdsp_pas { + status = "okay"; + firmware-name = "qcom/db845c/cdsp.mdt"; +}; + +&dsi0 { + status = "okay"; + vdda-supply = <&vreg_l26a_1p2>; + +#if 0 + qcom,dual-dsi-mode; + qcom,master-dsi; +#endif + + ports { + port@1 { + endpoint { + remote-endpoint = <<9611_a>; + data-lanes = <0 1 2 3>; + }; + }; + }; +}; + +&dsi0_phy { + status = "okay"; + vdds-supply = <&vreg_l1a_0p875>; +}; + +#if 0 +&dsi1 { + status = "okay"; + vdda-supply = <&vreg_l26a_1p2>; + + qcom,dual-dsi-mode; + + ports { + port@1 { + endpoint { + remote-endpoint = <<9611_b>; + data-lanes = <0 1 2 3>; + }; + }; + }; +}; + +&dsi1_phy { + status = "okay"; + vdds-supply = <&vreg_l1a_0p875>; +}; +#endif + &gcc { protected-clocks = , , ; }; +&i2c10 { + status = "okay"; + clock-frequency = <400000>; + + hdmi-bridge@3b { + compatible = "lt,lt9611"; + reg = <0x3b>; + + interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>; + + reset-gpios = <&tlmm 128 GPIO_ACTIVE_HIGH>; + + vdd-supply = <<9611_1v8>; + vcc-supply = <<9611_3v3>; + + pinctrl-names = "default"; + pinctrl-0 = <<9611_irq_pin>, <&dsi_sw_sel>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lt9611_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + + port@1 { + reg = <1>; + + lt9611_a: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + +#if 0 + port@2 { + reg = <2>; + + lt9611_b: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; +#endif + }; + }; +}; + +&mdss { + status = "okay"; +}; + +&mdss_mdp { + status = "okay"; +}; + &pcie0 { status = "okay"; perst-gpio = <&tlmm 35 GPIO_ACTIVE_LOW>; @@ -381,6 +503,12 @@ }; &tlmm { + lt9611_irq_pin: lt9611-irq { + pins = "gpio84"; + function = "gpio"; + bias-disable; + }; + pcie0_pwren_state: pcie0-pwren { pins = "gpio90"; function = "gpio"; @@ -440,6 +568,15 @@ function = "gpio"; bias-pull-up; }; + + dsi_sw_sel: dsi-sw-sel { + pins = "gpio120"; + function = "gpio"; + + drive-strength = <2>; + bias-disable; + output-high; + }; }; &uart6 { @@ -534,6 +671,14 @@ /* PINCTRL - additions to nodes defined in sdm845.dtsi */ +&qup_i2c10_default { + pinconf { + pins = "gpio55", "gpio56"; + drive-strength = <2>; + bias-disable; + }; +}; + &qup_uart6_default { pinmux { pins = "gpio45", "gpio46", "gpio47", "gpio48"; -- cgit v1.2.3 From c7bbaf8befc487a51ad24e9e34ab8784c1cd5bb7 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 23 Mar 2019 10:19:45 -0700 Subject: arm64: defconfig: Enable LT9611 driver Signed-off-by: Bjorn Andersson --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2d9c39033c1a..b2a10685f1bd 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -504,6 +504,7 @@ CONFIG_DRM_SUN8I_DW_HDMI=m CONFIG_DRM_SUN8I_MIXER=m CONFIG_DRM_TEGRA=m CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_LONTIUM_LT9611=m CONFIG_DRM_SII902X=m CONFIG_DRM_I2C_ADV7511=m CONFIG_DRM_VC4=m -- cgit v1.2.3 From 7a2dec2da55f307236ca18b9b1abfbe5199c6c7a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 17 Apr 2019 14:23:42 +0100 Subject: drm/bridge: lt9611: Add hdmi audio codec support Signed-off-by: Srinivas Kandagatla --- drivers/gpu/drm/bridge/Kconfig | 1 + drivers/gpu/drm/bridge/lt9611.c | 120 +++++++++++++++++++++++++++++++++------- 2 files changed, 101 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 994bda728e80..0fa721d60183 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -46,6 +46,7 @@ config DRM_DUMB_VGA_DAC config DRM_LONTIUM_LT9611 tristate "Lontium LT9611 DSI/HDMI bridge" + select SND_SOC_HDMI_CODEC if SND_SOC depends on OF select DRM_PANEL_BRIDGE select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/lt9611.c b/drivers/gpu/drm/bridge/lt9611.c index 348ec76a3a3d..748ff21fcd17 100644 --- a/drivers/gpu/drm/bridge/lt9611.c +++ b/drivers/gpu/drm/bridge/lt9611.c @@ -19,6 +19,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -46,6 +51,7 @@ struct lt9611 { struct device_node *dsi1_node; struct mipi_dsi_device *dsi0; struct mipi_dsi_device *dsi1; + struct platform_device *audio_pdev; bool ac_mode; @@ -543,23 +549,6 @@ static int lt9611_power_off(struct lt9611 *lt9611) return ret; } -static void lt9611_i2s_init(struct lt9611 *lt9611) -{ - const struct reg_sequence init[] = { - { 0xff, 0x82 }, - { 0xd6, 0x8c }, - { 0xd7, 0x04 }, - - { 0xff, 0x84 }, - { 0x06, 0x08 }, - { 0x07, 0x10 }, - - { 0x34, 0xd4 }, - }; - - regmap_multi_reg_write(lt9611->regmap, init, ARRAY_SIZE(init)); -} - static void lt9611_reset(struct lt9611 *lt9611) { gpiod_set_value_cansleep(lt9611->reset_gpio, 1); @@ -1034,6 +1023,98 @@ static int lt9611_read_device_rev(struct lt9611 *lt9611) return ret; } +int lt9611_hdmi_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) +{ + return 0; +} + +static int lt9611_audio_startup(struct device *dev, void *data) +{ + struct lt9611 *lt9611 = data; + const struct reg_sequence init[] = { + { 0xff, 0x82 }, + { 0xd6, 0x8c }, + { 0xd7, 0x04 }, + + { 0xff, 0x84 }, + { 0x06, 0x18 }, + { 0x07, 0xf0 }, + + { 0x34, 0xd4 }, + }; + + regmap_multi_reg_write(lt9611->regmap, init, ARRAY_SIZE(init)); + + return 0; +} + +static void lt9611_audio_shutdown(struct device *dev, void *data) +{ + struct lt9611 *lt9611 = data; + const struct reg_sequence exit[] = { + { 0xff, 0x84 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + }; + + regmap_multi_reg_write(lt9611->regmap, exit, ARRAY_SIZE(exit)); +} + +static int lt9611_hdmi_i2s_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + +pr_err("DEBUG: %s: %d \n", __func__, __LINE__); + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + +static const struct hdmi_codec_ops lt9611_codec_ops = { + .hw_params = lt9611_hdmi_hw_params, + .audio_shutdown = lt9611_audio_shutdown, + .audio_startup = lt9611_audio_startup, + .get_dai_id = lt9611_hdmi_i2s_get_dai_id, +}; + +static struct hdmi_codec_pdata codec_data = { + .ops = <9611_codec_ops, + .max_i2s_channels = 8, + .i2s = 1, +}; + +int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611) +{ + codec_data.data = lt9611; + lt9611->audio_pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + return PTR_ERR_OR_ZERO(lt9611->audio_pdev); +} + +void lt9611_audio_exit(struct lt9611 *lt9611) +{ + if (lt9611->audio_pdev) { + platform_device_unregister(lt9611->audio_pdev); + lt9611->audio_pdev = NULL; + } +} + static int lt9611_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1087,7 +1168,6 @@ static int lt9611_probe(struct i2c_client *client, goto err_disable_regulators; } - lt9611_i2s_init(lt9611); ret = devm_request_threaded_irq(dev, client->irq, NULL, lt9611_irq_thread_handler, @@ -1106,7 +1186,7 @@ static int lt9611_probe(struct i2c_client *client, lt9611_enable_hpd_interrupts(lt9611); - return 0; + return lt9611_audio_init(dev, lt9611); err_disable_regulators: regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies); @@ -1122,7 +1202,7 @@ static int lt9611_remove(struct i2c_client *client) struct lt9611 *lt9611 = i2c_get_clientdata(client); disable_irq(client->irq); - + lt9611_audio_exit(lt9611); drm_bridge_remove(<9611->bridge); regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies); -- cgit v1.2.3 From ae3b48b0aba2e5b8494dfe569ae18f672c5cbef9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 17 Apr 2019 15:01:12 +0100 Subject: arm64: dts: db845: add hdmi digital audio support Signed-off-by: Srinivas Kandagatla --- arch/arm64/boot/dts/qcom/sdm845-db845c.dts | 46 +++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts index 3ff12933b282..bea2247a221e 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "sdm845.dtsi" #include "pm8998.dtsi" #include "pmi8998.dtsi" @@ -382,13 +384,55 @@ ; }; +&adsp_pas { + status = "okay"; +}; + +/* QUAT I2S Uses 4 I2S SD Lines for audio on LT9611 HDMI Bridge */ +&q6afedai { + qi2s@22 { + reg = <22>; + qcom,sd-lines = <0 1 2 3>; + }; +}; + +&sound { + compatible = "qcom,db845c-sndcard"; + pinctrl-0 = <&quat_mi2s_active &quat_mi2s_sd0_active &quat_mi2s_sd1_active &quat_mi2s_sd2_active &quat_mi2s_sd3_active>; + pinctrl-names = "default"; + model = "DB845c"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + hdmi-dai-link { + link-name = "HDMI"; + cpu { + sound-dai = <&q6afedai QUATERNARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <<9611_codec 0>; + }; + }; +}; + &i2c10 { status = "okay"; clock-frequency = <400000>; - hdmi-bridge@3b { + lt9611_codec: hdmi-bridge@3b { compatible = "lt,lt9611"; reg = <0x3b>; + #sound-dai-cells = <1>; interrupts-extended = <&tlmm 84 IRQ_TYPE_EDGE_FALLING>; -- cgit v1.2.3