Other Parts Discussed in Thread: SN65DSI84
Hi Sir/Madam,
I'm working on NXP IMX8M MINI EVK board on LINUX environment. We are working on the PANASYS LVDS display which is an custom display, for converting the MIPI-DSI data lines to LVDS data we are using SN65DSI83 as an bridge converter. We added the drivers with parameters for the bridge at the location (drivers/gpu/drm/bridge/sn65dsi83) and added the display parameters in the imx8mm-evk.dts file at the location (arch/arm64/boot/dts/Freescale). After adding the bridge driver and the dts file we are unable to see any display on the evk. Here I'm sharing the boot log and the dts file along with the patch.
// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright 2019 NXP */ /dts-v1/; #include <dt-bindings/usb/pd.h> #include "imx8mm.dtsi" / { model = "FSL i.MX8MM EVK board"; compatible = "fsl,imx8mm-evk", "fsl,imx8mm"; reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; rpmsg_reserved: rpmsg@0xb8000000 { no-map; reg = <0 0xb8000000 0 0x400000>; }; }; chosen { stdout-path = &uart2; }; leds { compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio_led>; status { label = "status"; gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>; default-state = "on"; }; }; modem_reset: modem-reset { compatible = "gpio-reset"; reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>; reset-delay-us = <2000>; reset-post-delay-ms = <40>; #reset-cells = <0>; }; ir_recv: ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ir_recv>; }; pcie0_refclk: pcie0-refclk { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <100000000>; }; reg_sd1_vmmc: sd1_regulator { compatible = "regulator-fixed"; regulator-name = "WLAN_EN"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; gpio = <&gpio2 10 GPIO_ACTIVE_HIGH>; off-on-delay-us = <20000>; startup-delay-us = <100>; enable-active-high; }; reg_usdhc2_vmmc: regulator-usdhc2 { compatible = "regulator-fixed"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_reg_usdhc2_vmmc>; regulator-name = "VSD_3V3"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; gpio = <&gpio2 19 GPIO_ACTIVE_HIGH>; off-on-delay-us = <20000>; enable-active-high; }; reg_audio_board: regulator-audio-board { compatible = "regulator-fixed"; regulator-name = "EXT_PWREN"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; enable-active-high; startup-delay-us = <300000>; gpio = <&pca6416 1 GPIO_ACTIVE_HIGH>; }; wm8524: audio-codec { #sound-dai-cells = <0>; compatible = "wlf,wm8524"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio_wlf>; wlf,mute-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>; }; bt_sco_codec: bt_sco_codec { #sound-dai-cells = <0>; compatible = "linux,bt-sco"; }; sound-bt-sco { compatible = "simple-audio-card"; simple-audio-card,name = "bt-sco-audio"; simple-audio-card,format = "i2s"; simple-audio-card,bitclock-inversion; simple-audio-card,frame-master = <&btcpu>; simple-audio-card,bitclock-master = <&btcpu>; btcpu: simple-audio-card,cpu { sound-dai = <&sai2>; dai-tdm-slot-num = <2>; dai-tdm-slot-width = <16>; }; simple-audio-card,codec { sound-dai = <&bt_sco_codec>; }; }; sound-wm8524 { compatible = "simple-audio-card"; simple-audio-card,name = "wm8524-audio"; simple-audio-card,format = "i2s"; simple-audio-card,frame-master = <&cpudai>; simple-audio-card,bitclock-master = <&cpudai>; simple-audio-card,widgets = "Line", "Left Line Out Jack", "Line", "Right Line Out Jack"; simple-audio-card,routing = "Left Line Out Jack", "LINEVOUTL", "Right Line Out Jack", "LINEVOUTR"; cpudai: simple-audio-card,cpu { sound-dai = <&sai3>; dai-tdm-slot-num = <2>; dai-tdm-slot-width = <32>; }; simple-audio-card,codec { sound-dai = <&wm8524>; clocks = <&clk IMX8MM_CLK_SAI3_ROOT>; }; }; sound-ak4458 { compatible = "fsl,imx-audio-ak4458"; model = "ak4458-audio"; audio-cpu = <&sai1>; audio-codec = <&ak4458_1>, <&ak4458_2>; ak4458,pdn-gpio = <&pca6416 4 GPIO_ACTIVE_HIGH>; }; sound-ak5558 { compatible = "fsl,imx-audio-ak5558"; model = "ak5558-audio"; audio-cpu = <&sai5>; audio-codec = <&ak5558>; status = "disabled"; }; sound-ak4497 { compatible = "fsl,imx-audio-ak4497"; model = "ak4497-audio"; audio-cpu = <&sai1>; audio-codec = <&ak4497>; status = "disabled"; }; sound-spdif { compatible = "fsl,imx-audio-spdif"; model = "imx-spdif"; spdif-controller = <&spdif1>; spdif-out; spdif-in; }; sound-micfil { compatible = "fsl,imx-audio-micfil"; model = "imx-audio-micfil"; cpu-dai = <&micfil>; }; }; &A53_0 { cpu-supply = <&buck2_reg>; }; &csi1_bridge { fsl,mipi-mode; status = "okay"; port { csi1_ep: endpoint { remote-endpoint = <&csi1_mipi_ep>; }; }; }; &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_fec1>; phy-mode = "rgmii-id"; phy-handle = <ðphy0>; fsl,magic-packet; status = "okay"; mdio { #address-cells = <1>; #size-cells = <0>; ethphy0: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0>; at803x,eee-disabled; at803x,vddio-1p8v; }; }; }; &pcie0{ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pcie0>; disable-gpio = <&gpio1 5 GPIO_ACTIVE_LOW>; reset-gpio = <&gpio4 21 GPIO_ACTIVE_LOW>; clocks = <&clk IMX8MM_CLK_PCIE1_ROOT>, <&clk IMX8MM_CLK_PCIE1_AUX>, <&clk IMX8MM_CLK_PCIE1_PHY>, <&pcie0_refclk>; clock-names = "pcie", "pcie_aux", "pcie_phy", "pcie_bus"; ext_osc = <1>; reserved-region = <&rpmsg_reserved>; status = "okay"; }; &pcie0_ep{ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pcie0>; clocks = <&clk IMX8MM_CLK_PCIE1_ROOT>, <&clk IMX8MM_CLK_PCIE1_AUX>, <&clk IMX8MM_CLK_PCIE1_PHY>, <&pcie0_refclk>; clock-names = "pcie", "pcie_aux", "pcie_phy", "pcie_bus"; ext_osc = <1>; status = "disabled"; }; &sai3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai3>; assigned-clocks = <&clk IMX8MM_CLK_SAI3>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <24576000>; status = "okay"; }; &sai2 { #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai2>; assigned-clocks = <&clk IMX8MM_CLK_SAI2>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <24576000>; status = "okay"; }; &sai1 { pinctrl-names = "default", "dsd"; pinctrl-0 = <&pinctrl_sai1>; pinctrl-1 = <&pinctrl_sai1_dsd>; assigned-clocks = <&clk IMX8MM_CLK_SAI1>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <49152000>; clocks = <&clk IMX8MM_CLK_SAI1_IPG>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_SAI1_ROOT>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_AUDIO_PLL1_OUT>, <&clk IMX8MM_AUDIO_PLL2_OUT>; clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k"; fsl,sai-multi-lane; fsl,dataline,dsd = <0 0xff 0xff 2 0xff 0x11>; dmas = <&sdma2 0 25 0>, <&sdma2 1 25 0>; status = "okay"; }; &sai5 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai5>; assigned-clocks = <&clk IMX8MM_CLK_SAI5>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <49152000>; clocks = <&clk IMX8MM_CLK_SAI5_IPG>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_SAI5_ROOT>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_AUDIO_PLL1_OUT>, <&clk IMX8MM_AUDIO_PLL2_OUT>; clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k", "pll11k"; fsl,sai-asynchronous; status = "disabled"; }; &sai6 { fsl,sai-monitor-spdif; fsl,sai-asynchronous; status = "okay"; }; &spdif1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spdif1>; assigned-clocks = <&clk IMX8MM_CLK_SPDIF1>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <24576000>; clocks = <&clk IMX8MM_CLK_AUDIO_AHB>, <&clk IMX8MM_CLK_24M>, <&clk IMX8MM_CLK_SPDIF1>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_AUDIO_AHB>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_CLK_DUMMY>, <&clk IMX8MM_AUDIO_PLL1_OUT>, <&clk IMX8MM_AUDIO_PLL2_OUT>; clock-names = "core", "rxtx0", "rxtx1", "rxtx2", "rxtx3", "rxtx4", "rxtx5", "rxtx6", "rxtx7", "spba", "pll8k", "pll11k"; status = "okay"; }; &micfil { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pdm>; assigned-clocks = <&clk IMX8MM_CLK_PDM>; assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; assigned-clock-rates = <196608000>; status = "okay"; }; &snvs_pwrkey { status = "okay"; }; &uart1 { /* BT */ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart1>; assigned-clocks = <&clk IMX8MM_CLK_UART1>; assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>; fsl,uart-has-rtscts; resets = <&modem_reset>; status = "okay"; }; &uart2 { /* console */ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart2>; status = "okay"; }; &uart3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart3>; assigned-clocks = <&clk IMX8MM_CLK_UART3>; assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_80M>; fsl,uart-has-rtscts; status = "okay"; }; &usbotg1 { dr_mode = "otg"; hnp-disable; srp-disable; adp-disable; usb-role-switch; picophy,pre-emp-curr-control = <3>; picophy,dc-vol-level-adjust = <7>; status = "okay"; port { usb1_drd_sw: endpoint { remote-endpoint = <&typec1_dr_sw>; }; }; }; &usdhc1 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_usdhc1_gpio>; pinctrl-1 = <&pinctrl_usdhc1_100mhz>, <&pinctrl_usdhc1_gpio>; pinctrl-2 = <&pinctrl_usdhc1_200mhz>, <&pinctrl_usdhc1_gpio>; bus-width = <4>; vmmc-supply = <®_sd1_vmmc>; pm-ignore-notify; keep-power-in-suspend; non-removable; status = "okay"; }; &usdhc2 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>; pinctrl-1 = <&pinctrl_usdhc2_100mhz>, <&pinctrl_usdhc2_gpio>; pinctrl-2 = <&pinctrl_usdhc2_200mhz>, <&pinctrl_usdhc2_gpio>; cd-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; bus-width = <4>; vmmc-supply = <®_usdhc2_vmmc>; status = "okay"; }; &usdhc3 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; bus-width = <8>; non-removable; status = "okay"; }; &wdog1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_wdog>; fsl,ext-reset-output; status = "okay"; }; &flexspi { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_flexspi0>; status = "okay"; flash0: mt25qu256aba@0 { reg = <0>; #address-cells = <1>; #size-cells = <1>; compatible = "jedec,spi-nor"; spi-max-frequency = <80000000>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; }; }; &i2c1 { clock-frequency = <400000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; status = "okay"; pmic@4b { compatible = "rohm,bd71847"; reg = <0x4b>; pinctrl-0 = <&pinctrl_pmic>; interrupt-parent = <&gpio1>; interrupts = <3 GPIO_ACTIVE_LOW>; rohm,reset-snvs-powered; regulators { buck1_reg: BUCK1 { regulator-name = "BUCK1"; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1300000>; regulator-boot-on; regulator-always-on; regulator-ramp-delay = <1250>; }; buck2_reg: BUCK2 { regulator-name = "BUCK2"; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1300000>; regulator-boot-on; regulator-always-on; regulator-ramp-delay = <1250>; rohm,dvs-run-voltage = <1000000>; rohm,dvs-idle-voltage = <900000>; }; buck3_reg: BUCK3 { // BUCK5 in datasheet regulator-name = "BUCK3"; regulator-min-microvolt = <700000>; regulator-max-microvolt = <1350000>; regulator-boot-on; regulator-always-on; }; buck4_reg: BUCK4 { // BUCK6 in datasheet regulator-name = "BUCK4"; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3300000>; regulator-boot-on; regulator-always-on; }; buck5_reg: BUCK5 { // BUCK7 in datasheet regulator-name = "BUCK5"; regulator-min-microvolt = <1605000>; regulator-max-microvolt = <1995000>; regulator-boot-on; regulator-always-on; }; buck6_reg: BUCK6 { // BUCK8 in datasheet regulator-name = "BUCK6"; regulator-min-microvolt = <800000>; regulator-max-microvolt = <1400000>; regulator-boot-on; regulator-always-on; }; ldo1_reg: LDO1 { regulator-name = "LDO1"; regulator-min-microvolt = <1600000>; regulator-max-microvolt = <1900000>; regulator-boot-on; regulator-always-on; }; ldo2_reg: LDO2 { regulator-name = "LDO2"; regulator-min-microvolt = <800000>; regulator-max-microvolt = <900000>; regulator-boot-on; regulator-always-on; }; ldo3_reg: LDO3 { regulator-name = "LDO3"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-boot-on; regulator-always-on; }; ldo4_reg: LDO4 { regulator-name = "LDO4"; regulator-min-microvolt = <900000>; regulator-max-microvolt = <1800000>; regulator-boot-on; regulator-always-on; }; ldo6_reg: LDO6 { regulator-name = "LDO6"; regulator-min-microvolt = <900000>; regulator-max-microvolt = <1800000>; regulator-boot-on; regulator-always-on; }; }; }; }; &i2c2 { clock-frequency = <400000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c2>; status = "okay"; dsi_lvds_bridge: sn65dsi84@2c { compatible = "ti,sn65dsi83"; reg = <0x2c>; ti,dsi-lanes = <4>; ti,lvds-format = <1>; ti,lvds-bpp = <24>; ti,width-mm = <217>; ti,height-mm = <136>; enable-gpios = <&gpio3 0 GPIO_ACTIVE_HIGH>; interrupt-parent = <&gpio3>; interrupts = <6 IRQ_TYPE_LEVEL_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_lvds>; status = "okay"; display-timings { native-mode = <&lvds0_g101evn010>; /* AUO G101EVN01.0 */ lvds0_g101evn010: timing@0 { clock-frequency = <69000000>; hactive = <1280>; vactive = <800>; hfront-porch = <120>; hback-porch = <1>; hsync-len = <8>; vback-porch = <10>; vfront-porch = <1>; vsync-len = <6>; hsync-active = <1>; vsync-active = <1>; de-active = <1>; pixelclk-active = <0>; }; /* Fusion 10" F10A-0102 */ lvds0_hsd101pfw2: timing@1 { clock-frequency = <45000000>; hactive = <1024>; vactive = <600>; hfront-porch = <120>; hback-porch = <1>; hsync-len = <8>; vback-porch = <10>; vfront-porch = <1>; vsync-len = <6>; hsync-active = <1>; vsync-active = <1>; de-active = <1>; pixelclk-active = <0>; }; }; port { dsi_lvds_bridge_in: endpoint { remote-endpoint = <&mipi_dsi_lvds_out>; }; }; adv_bridge: adv7535@3d { compatible = "adi,adv7533"; reg = <0x3d>; adi,addr-cec = <0x3b>; adi,dsi-lanes = <4>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c2_synaptics_dsx_io>; interrupt-parent = <&gpio1>; interrupts = <9 IRQ_TYPE_LEVEL_LOW>; status = "disable"; port { adv7535_from_dsim: endpoint { remote-endpoint = <&dsim_to_adv7535>; }; }; }; ptn5110: tcpc@50 { compatible = "nxp,ptn5110"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec1>; reg = <0x50>; interrupt-parent = <&gpio2>; interrupts = <11 8>; status = "okay"; port { typec1_dr_sw: endpoint { remote-endpoint = <&usb1_drd_sw>; }; }; typec1_con: connector { compatible = "usb-c-connector"; label = "USB-C"; power-role = "dual"; data-role = "dual"; try-power-role = "sink"; source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>; sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM) PDO_VAR(5000, 20000, 3000)>; op-sink-microwatt = <15000000>; self-powered; }; }; }; &i2c3 { clock-frequency = <100000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c3>; status = "okay"; pca6416: gpio@20 { compatible = "ti,tca6416"; reg = <0x20>; gpio-controller; #gpio-cells = <2>; vcc-supply = <&buck4_reg>; }; ak4458_1: ak4458@10 { compatible = "asahi-kasei,ak4458"; reg = <0x10>; AVDD-supply = <®_audio_board>; DVDD-supply = <®_audio_board>; }; ak4458_2: ak4458@12 { compatible = "asahi-kasei,ak4458"; reg = <0x12>; AVDD-supply = <®_audio_board>; DVDD-supply = <®_audio_board>; }; ak5558: ak5558@13 { compatible = "asahi-kasei,ak5558"; reg = <0x13>; ak5558,pdn-gpio = <&pca6416 3 GPIO_ACTIVE_HIGH>; AVDD-supply = <®_audio_board>; DVDD-supply = <®_audio_board>; }; ak4497: ak4497@11 { compatible = "asahi-kasei,ak4497"; reg = <0x11>; ak4497,pdn-gpio = <&pca6416 5 GPIO_ACTIVE_HIGH>; AVDD-supply = <®_audio_board>; DVDD-supply = <®_audio_board>; }; ov5640_mipi: ov5640_mipi@3c { compatible = "ovti,ov5640_mipi"; reg = <0x3c>; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_csi_pwn>, <&pinctrl_csi_rst>; clocks = <&clk IMX8MM_CLK_CLKO1>; clock-names = "csi_mclk"; assigned-clocks = <&clk IMX8MM_CLK_CLKO1>; assigned-clock-parents = <&clk IMX8MM_CLK_24M>; assigned-clock-rates = <24000000>; csi_id = <0>; pwn-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; mclk = <24000000>; mclk_source = <0>; port { ov5640_mipi1_ep: endpoint { remote-endpoint = <&mipi1_sensor_ep>; }; }; }; }; &iomuxc { pinctrl-names = "default"; pinctrl_csi_pwn: csi_pwn_grp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO07_GPIO1_IO7 0x19 >; }; pinctrl_csi_rst: csi_rst_grp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO06_GPIO1_IO6 0x19 MX8MM_IOMUXC_GPIO1_IO14_CCMSRCGPCMIX_CLKO1 0x59 >; }; pinctrl_ir_recv: ir-recv { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO13_GPIO1_IO13 0x4f >; }; pinctrl_fec1: fec1grp { fsl,pins = < MX8MM_IOMUXC_ENET_MDC_ENET1_MDC 0x3 MX8MM_IOMUXC_ENET_MDIO_ENET1_MDIO 0x3 MX8MM_IOMUXC_ENET_TD3_ENET1_RGMII_TD3 0x1f MX8MM_IOMUXC_ENET_TD2_ENET1_RGMII_TD2 0x1f MX8MM_IOMUXC_ENET_TD1_ENET1_RGMII_TD1 0x1f MX8MM_IOMUXC_ENET_TD0_ENET1_RGMII_TD0 0x1f MX8MM_IOMUXC_ENET_RD3_ENET1_RGMII_RD3 0x91 MX8MM_IOMUXC_ENET_RD2_ENET1_RGMII_RD2 0x91 MX8MM_IOMUXC_ENET_RD1_ENET1_RGMII_RD1 0x91 MX8MM_IOMUXC_ENET_RD0_ENET1_RGMII_RD0 0x91 MX8MM_IOMUXC_ENET_TXC_ENET1_RGMII_TXC 0x1f MX8MM_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x91 MX8MM_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x91 MX8MM_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x1f MX8MM_IOMUXC_SAI2_RXC_GPIO4_IO22 0x19 >; }; pinctrl_flexspi0: flexspi0grp { fsl,pins = < MX8MM_IOMUXC_NAND_ALE_QSPI_A_SCLK 0x1c2 MX8MM_IOMUXC_NAND_CE0_B_QSPI_A_SS0_B 0x82 MX8MM_IOMUXC_NAND_DATA00_QSPI_A_DATA0 0x82 MX8MM_IOMUXC_NAND_DATA01_QSPI_A_DATA1 0x82 MX8MM_IOMUXC_NAND_DATA02_QSPI_A_DATA2 0x82 MX8MM_IOMUXC_NAND_DATA03_QSPI_A_DATA3 0x82 >; }; pinctrl_gpio_led: gpioledgrp { fsl,pins = < MX8MM_IOMUXC_NAND_READY_B_GPIO3_IO16 0x19 >; }; pinctrl_lvds: lvdsgrp { fsl,pins = < /* SN65DSI83 enable */ MX8MM_IOMUXC_NAND_ALE_GPIO3_IO0 0x19 /* SN65DSI83 interrupt */ MX8MM_IOMUXC_NAND_DATA00_GPIO3_IO6 0x19 >; }; pinctrl_gpio_wlf: gpiowlfgrp { fsl,pins = < MX8MM_IOMUXC_I2C4_SDA_GPIO5_IO21 0xd6 >; }; pinctrl_i2c1: i2c1grp { fsl,pins = < MX8MM_IOMUXC_I2C1_SCL_I2C1_SCL 0x400001c3 MX8MM_IOMUXC_I2C1_SDA_I2C1_SDA 0x400001c3 >; }; pinctrl_i2c2: i2c2grp { fsl,pins = < MX8MM_IOMUXC_I2C2_SCL_I2C2_SCL 0x400001c3 MX8MM_IOMUXC_I2C2_SDA_I2C2_SDA 0x400001c3 >; }; pinctrl_i2c3: i2c3grp { fsl,pins = < MX8MM_IOMUXC_I2C3_SCL_I2C3_SCL 0x400001c3 MX8MM_IOMUXC_I2C3_SDA_I2C3_SDA 0x400001c3 >; }; pinctrl_mipi_dsi_en: mipi_dsi_en { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO08_GPIO1_IO8 0x16 >; }; pinctrl_i2c2_synaptics_dsx_io: synaptics_dsx_iogrp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x19 /* Touch int */ >; }; pinctrl_pcie0: pcie0grp { fsl,pins = < MX8MM_IOMUXC_I2C4_SCL_PCIE1_CLKREQ_B 0x61 /* open drain, pull up */ MX8MM_IOMUXC_GPIO1_IO05_GPIO1_IO5 0x41 MX8MM_IOMUXC_SAI2_RXFS_GPIO4_IO21 0x41 >; }; pinctrl_pmic: pmicirq { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO03_GPIO1_IO3 0x41 >; }; pinctrl_reg_usdhc2_vmmc: regusdhc2vmmc { fsl,pins = < MX8MM_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x41 >; }; pinctrl_sai2: sai2grp { fsl,pins = < MX8MM_IOMUXC_SAI2_TXC_SAI2_TX_BCLK 0xd6 MX8MM_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC 0xd6 MX8MM_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0 0xd6 MX8MM_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0 0xd6 >; }; pinctrl_sai3: sai3grp { fsl,pins = < MX8MM_IOMUXC_SAI3_TXFS_SAI3_TX_SYNC 0xd6 MX8MM_IOMUXC_SAI3_TXC_SAI3_TX_BCLK 0xd6 MX8MM_IOMUXC_SAI3_MCLK_SAI3_MCLK 0xd6 MX8MM_IOMUXC_SAI3_TXD_SAI3_TX_DATA0 0xd6 >; }; pinctrl_sai1: sai1grp { fsl,pins = < MX8MM_IOMUXC_SAI1_MCLK_SAI1_MCLK 0xd6 MX8MM_IOMUXC_SAI1_TXFS_SAI1_TX_SYNC 0xd6 MX8MM_IOMUXC_SAI1_RXD7_SAI1_TX_SYNC 0xd6 MX8MM_IOMUXC_SAI1_TXC_SAI1_TX_BCLK 0xd6 MX8MM_IOMUXC_SAI1_TXD0_SAI1_TX_DATA0 0xd6 MX8MM_IOMUXC_SAI1_TXD1_SAI1_TX_DATA1 0xd6 MX8MM_IOMUXC_SAI1_TXD2_SAI1_TX_DATA2 0xd6 MX8MM_IOMUXC_SAI1_TXD3_SAI1_TX_DATA3 0xd6 MX8MM_IOMUXC_SAI1_TXD4_SAI1_TX_DATA4 0xd6 MX8MM_IOMUXC_SAI1_TXD5_SAI1_TX_DATA5 0xd6 MX8MM_IOMUXC_SAI1_TXD6_SAI1_TX_DATA6 0xd6 MX8MM_IOMUXC_SAI1_TXD7_SAI1_TX_DATA7 0xd6 >; }; pinctrl_sai1_dsd: sai1grp_dsd { fsl,pins = < MX8MM_IOMUXC_SAI1_MCLK_SAI1_MCLK 0xd6 MX8MM_IOMUXC_SAI1_TXFS_SAI1_TX_SYNC 0xd6 MX8MM_IOMUXC_SAI1_RXD7_SAI1_TX_DATA4 0xd6 MX8MM_IOMUXC_SAI1_TXC_SAI1_TX_BCLK 0xd6 MX8MM_IOMUXC_SAI1_TXD0_SAI1_TX_DATA0 0xd6 MX8MM_IOMUXC_SAI1_TXD1_SAI1_TX_DATA1 0xd6 MX8MM_IOMUXC_SAI1_TXD2_SAI1_TX_DATA2 0xd6 MX8MM_IOMUXC_SAI1_TXD3_SAI1_TX_DATA3 0xd6 MX8MM_IOMUXC_SAI1_TXD4_SAI1_TX_DATA4 0xd6 MX8MM_IOMUXC_SAI1_TXD5_SAI1_TX_DATA5 0xd6 MX8MM_IOMUXC_SAI1_TXD6_SAI1_TX_DATA6 0xd6 MX8MM_IOMUXC_SAI1_TXD7_SAI1_TX_DATA7 0xd6 >; }; pinctrl_sai5: sai5grp { fsl,pins = < MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0xd6 MX8MM_IOMUXC_SAI5_RXC_SAI5_RX_BCLK 0xd6 MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0xd6 MX8MM_IOMUXC_SAI5_RXD0_SAI5_RX_DATA0 0xd6 MX8MM_IOMUXC_SAI5_RXD1_SAI5_RX_DATA1 0xd6 MX8MM_IOMUXC_SAI5_RXD2_SAI5_RX_DATA2 0xd6 MX8MM_IOMUXC_SAI5_RXD3_SAI5_RX_DATA3 0xd6 >; }; pinctrl_pdm: pdmgrp { fsl,pins = < MX8MM_IOMUXC_SAI5_MCLK_SAI5_MCLK 0xd6 MX8MM_IOMUXC_SAI5_RXC_PDM_CLK 0xd6 MX8MM_IOMUXC_SAI5_RXFS_SAI5_RX_SYNC 0xd6 MX8MM_IOMUXC_SAI5_RXD0_PDM_DATA0 0xd6 MX8MM_IOMUXC_SAI5_RXD1_PDM_DATA1 0xd6 MX8MM_IOMUXC_SAI5_RXD2_PDM_DATA2 0xd6 MX8MM_IOMUXC_SAI5_RXD3_PDM_DATA3 0xd6 >; }; pinctrl_spdif1: spdif1grp { fsl,pins = < MX8MM_IOMUXC_SPDIF_TX_SPDIF1_OUT 0xd6 MX8MM_IOMUXC_SPDIF_RX_SPDIF1_IN 0xd6 >; }; pinctrl_typec1: typec1grp { fsl,pins = < MX8MM_IOMUXC_SD1_STROBE_GPIO2_IO11 0x159 >; }; pinctrl_uart1: uart1grp { fsl,pins = < MX8MM_IOMUXC_UART1_RXD_UART1_DCE_RX 0x140 MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x140 MX8MM_IOMUXC_UART3_RXD_UART1_DCE_CTS_B 0x140 MX8MM_IOMUXC_UART3_TXD_UART1_DCE_RTS_B 0x140 MX8MM_IOMUXC_SD1_DATA4_GPIO2_IO6 0x19 >; }; pinctrl_uart2: uart2grp { fsl,pins = < MX8MM_IOMUXC_UART2_RXD_UART2_DCE_RX 0x140 MX8MM_IOMUXC_UART2_TXD_UART2_DCE_TX 0x140 >; }; pinctrl_uart3: uart3grp { fsl,pins = < MX8MM_IOMUXC_ECSPI1_SCLK_UART3_DCE_RX 0x140 MX8MM_IOMUXC_ECSPI1_MOSI_UART3_DCE_TX 0x140 MX8MM_IOMUXC_ECSPI1_SS0_UART3_DCE_RTS_B 0x140 MX8MM_IOMUXC_ECSPI1_MISO_UART3_DCE_CTS_B 0x140 >; }; pinctrl_usdhc1_gpio: usdhc1grpgpio { fsl,pins = < MX8MM_IOMUXC_SD1_RESET_B_GPIO2_IO10 0x41 >; }; pinctrl_usdhc1: usdhc1grp { fsl,pins = < MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x190 MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d0 MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d0 MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d0 MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d0 MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d0 >; }; pinctrl_usdhc1_100mhz: usdhc1grp100mhz { fsl,pins = < MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x194 MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d4 MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d4 MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d4 MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d4 MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d4 >; }; pinctrl_usdhc1_200mhz: usdhc1grp200mhz { fsl,pins = < MX8MM_IOMUXC_SD1_CLK_USDHC1_CLK 0x196 MX8MM_IOMUXC_SD1_CMD_USDHC1_CMD 0x1d6 MX8MM_IOMUXC_SD1_DATA0_USDHC1_DATA0 0x1d6 MX8MM_IOMUXC_SD1_DATA1_USDHC1_DATA1 0x1d6 MX8MM_IOMUXC_SD1_DATA2_USDHC1_DATA2 0x1d6 MX8MM_IOMUXC_SD1_DATA3_USDHC1_DATA3 0x1d6 >; }; pinctrl_usdhc2_gpio: usdhc2grpgpio { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15 0x1c4 >; }; pinctrl_usdhc2: usdhc2grp { fsl,pins = < MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x190 MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d0 MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d0 MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d0 MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d0 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d0 MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc2_100mhz: usdhc2grp100mhz { fsl,pins = < MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x194 MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d4 MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4 MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4 MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4 MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc2_200mhz: usdhc2grp200mhz { fsl,pins = < MX8MM_IOMUXC_SD2_CLK_USDHC2_CLK 0x196 MX8MM_IOMUXC_SD2_CMD_USDHC2_CMD 0x1d6 MX8MM_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d6 MX8MM_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d6 MX8MM_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d6 MX8MM_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d6 MX8MM_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x1d0 >; }; pinctrl_usdhc3: usdhc3grp { fsl,pins = < MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x190 MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d0 MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d0 MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d0 MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d0 MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d0 MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d0 MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d0 MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d0 MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d0 MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x190 >; }; pinctrl_usdhc3_100mhz: usdhc3grp100mhz { fsl,pins = < MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x194 MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d4 MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d4 MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d4 MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d4 MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d4 MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d4 MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d4 MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d4 MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d4 MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x194 >; }; pinctrl_usdhc3_200mhz: usdhc3grp200mhz { fsl,pins = < MX8MM_IOMUXC_NAND_WE_B_USDHC3_CLK 0x196 MX8MM_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d6 MX8MM_IOMUXC_NAND_DATA04_USDHC3_DATA0 0x1d6 MX8MM_IOMUXC_NAND_DATA05_USDHC3_DATA1 0x1d6 MX8MM_IOMUXC_NAND_DATA06_USDHC3_DATA2 0x1d6 MX8MM_IOMUXC_NAND_DATA07_USDHC3_DATA3 0x1d6 MX8MM_IOMUXC_NAND_RE_B_USDHC3_DATA4 0x1d6 MX8MM_IOMUXC_NAND_CE2_B_USDHC3_DATA5 0x1d6 MX8MM_IOMUXC_NAND_CE3_B_USDHC3_DATA6 0x1d6 MX8MM_IOMUXC_NAND_CLE_USDHC3_DATA7 0x1d6 MX8MM_IOMUXC_NAND_CE1_B_USDHC3_STROBE 0x196 >; }; lvds_backlight: lvds_backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 100000 0>; brightness-levels = < 0 15 23 31 39 47 55 63 71 79 87 95 103 111 119 127 135 143 151 159 167 175 183 191 199 207 215 223 231 239 247 255>; default-brightness-level = <127>; // 125 }; pinctrl_pwm1: pwmgrp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO01_PWM1_OUT 0x16 >; }; pinctrl_wdog: wdoggrp { fsl,pins = < MX8MM_IOMUXC_GPIO1_IO02_WDOG1_WDOG_B 0xc6 >; }; }; &lcdif { status = "okay"; }; &mipi_csi_1 { #address-cells = <1>; #size-cells = <0>; status = "okay"; port { mipi1_sensor_ep: endpoint@1 { remote-endpoint = <&ov5640_mipi1_ep>; data-lanes = <2>; csis-hs-settle = <13>; csis-clk-settle = <2>; csis-wclk; }; csi1_mipi_ep: endpoint@2 { remote-endpoint = <&csi1_ep>; }; }; }; &mipi_dsi { status = "okay"; port@1 { dsim_to_adv7535: endpoint { remote-endpoint = <&adv7535_from_dsim>; attach-bridge; }; }; port@2 { mipi_dsi_lvds_out: endpoint { remote-endpoint = <&dsi_lvds_bridge_in>; }; }; &vpu_g1 { status = "okay"; }; &vpu_g2 { status = "okay"; }; &vpu_h1 { status = "okay"; }; &gpu { status = "okay"; };
From e808b65935978dc0ca28777320c6c2defb8ad569 Mon Sep 17 00:00:00 2001 From: Matteo Lisi <matteo.lisi@engicam.com> Date: Wed, 6 Feb 2019 15:35:17 0100 Subject: [PATCH 1/3] add sn65dsi83 driver and drm fix --- drivers/gpu/drm/Makefile | 2 - drivers/gpu/drm/bridge/Kconfig | 16 drivers/gpu/drm/bridge/Makefile | 3 drivers/gpu/drm/bridge/sec-dsim.c | 4 - drivers/gpu/drm/bridge/sn65dsi83.c | 666 drivers/gpu/drm/bridge/sn65dsi83/Kconfig | 7 drivers/gpu/drm/bridge/sn65dsi83/Makefile | 2 .../gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.c | 400 .../gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.h | 55 .../gpu/drm/bridge/sn65dsi83/sn65dsi83_drv.c | 433 .../drm/bridge/sn65dsi83/sn65dsi83_timing.h | 33 drivers/gpu/drm/bridge/sn65dsi84-dsi2lvds.c | 210 drivers/gpu/drm/drm_modes.c | 2 - drivers/gpu/drm/drm_notify.c | 43 include/drm/drmP.h | 4 15 files changed, 1876 insertions( ), 4 deletions(-) create mode 100644 drivers/gpu/drm/bridge/sn65dsi83.c create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/Kconfig create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/Makefile create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.c create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.h create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_drv.c create mode 100644 drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_timing.h create mode 100644 drivers/gpu/drm/bridge/sn65dsi84-dsi2lvds.c create mode 100644 drivers/gpu/drm/drm_notify.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c5da7b5a5..02ab6dbd9 100644 --- a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile @@ -18,7 18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ - drm_syncobj.o drm_lease.o drm_syncobj.o drm_lease.o drm_notify.o drm-$(CONFIG_DRM_LIB_RANDOM) = lib/drm_random.o drm-$(CONFIG_DRM_VM) = drm_vm.o diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 593e9a4e9..d711bf187 100644 --- a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig @@ -134,4 134,20 @@ config DRM_ITE_IT6263 ---help--- ITE IT6263 bridge chip driver. config DRM_TI_SN65DSI84 tristate "SN65DSI84 DSI to LVDS bridge (old)" select REGMAP_I2C select DRM_MIPI_DSI ---help--- Support for the Texas Intruments SN65DSI84 bridge. config DRM_TI_SN65DSI83 tristate "SN65DSI83/4 DSI to LVDS bridge (suggested)" select REGMAP_I2C select DRM_MIPI_DSI ---help--- Support for the Texas Intruments SN65DSI84 and SN65DSI83 bridges. source "drivers/gpu/drm/bridge/sn65dsi83/Kconfig" endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index d684dce13..a151c13d1 100644 --- a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile @@ -16,3 16,6 @@ obj-$(CONFIG_DRM_ITE_IT6263) = it6263.o obj-$(CONFIG_DRM_NWL_DSI) = nwl-dsi.o obj-$(CONFIG_DRM_SEC_MIPI_DSIM) = sec-dsim.o obj-$(CONFIG_DRM_NXP_SEIKO_43WVFIG) = nxp-seiko-43wvfig.o obj-$(CONFIG_DRM_TI_SN65DSI84) = sn65dsi84-dsi2lvds.o obj-$(CONFIG_DRM_TI_SN65DSI83) = sn65dsi83.o obj-$(CONFIG_DRM_I2C_SN65DSI83) = sn65dsi83/ diff --git a/drivers/gpu/drm/bridge/sec-dsim.c b/drivers/gpu/drm/bridge/sec-dsim.c index ea4966a5d..dd5dd3a76 100644 --- a/drivers/gpu/drm/bridge/sec-dsim.c b/drivers/gpu/drm/bridge/sec-dsim.c @@ -469,14 469,14 @@ static int sec_mipi_dsim_host_attach(struct mipi_dsi_host *host, if (dsim->channel) return -EINVAL; - /* if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO) || !((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) || (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) { dev_err(dev, "unsupported dsi mode\n"); return -EINVAL; } - */ if (dsi->format != MIPI_DSI_FMT_RGB888 && dsi->format != MIPI_DSI_FMT_RGB565 && dsi->format != MIPI_DSI_FMT_RGB666 && diff --git a/drivers/gpu/drm/bridge/sn65dsi83.c b/drivers/gpu/drm/bridge/sn65dsi83.c new file mode 100644 index 000000000..789ae6404 --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83.c @@ -0,0 1,666 @@ /* * sn65dsi83.c - DVI output chip * * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <drm/drmP.h> #include <drm/drm_mode.h> #include <linux/module.h> #include <linux/clk.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/of_gpio.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/input-polldev.h> #include <linux/gpio.h> #include <linux/fb.h> #include <linux/kthread.h> #include <video/display_timing.h> #include <video/of_videomode.h> #include <video/videomode.h> /* register definitions according to the sn65dsi83 data sheet */ #define SN_SOFT_RESET 0x09 #define SN_CLK_SRC 0x0a #define SN_CLK_DIV 0x0b #define SN_PLL_EN 0x0d #define SN_DSI_LANES 0x10 #define SN_DSI_EQ 0x11 #define SN_DSI_CLK 0x12 #define SN_FORMAT 0x18 #define SN_LVDS_VOLTAGE 0x19 #define SN_LVDS_TERM 0x1a #define SN_LVDS_CM_VOLTAGE 0x1b #define SN_HACTIVE_LOW 0x20 #define SN_HACTIVE_HIGH 0x21 #define SN_VACTIVE_LOW 0x24 #define SN_VACTIVE_HIGH 0x25 #define SN_SYNC_DELAY_LOW 0x28 #define SN_SYNC_DELAY_HIGH 0x29 #define SN_HSYNC_LOW 0x2c #define SN_HSYNC_HIGH 0x2d #define SN_VSYNC_LOW 0x30 #define SN_VSYNC_HIGH 0x31 #define SN_HBP 0x34 #define SN_VBP 0x36 #define SN_HFP 0x38 #define SN_VFP 0x3a #define SN_TEST_PATTERN 0x3c #define SN_IRQ_EN 0xe0 #define SN_IRQ_MASK 0xe1 #define SN_IRQ_STAT 0xe5 static const char *client_name = "sn65dsi83"; static const unsigned char registers_to_show[] = { SN_SOFT_RESET, SN_CLK_SRC, SN_CLK_DIV, SN_PLL_EN, SN_DSI_LANES, SN_DSI_EQ, SN_DSI_CLK, SN_FORMAT, SN_LVDS_VOLTAGE, SN_LVDS_TERM, SN_LVDS_CM_VOLTAGE, 0, SN_HACTIVE_LOW, 0, SN_VACTIVE_LOW, 0, SN_SYNC_DELAY_LOW, 0, SN_HSYNC_LOW, 0, SN_VSYNC_LOW, SN_HBP, SN_VBP, SN_HFP, SN_VFP, SN_TEST_PATTERN, SN_IRQ_EN, SN_IRQ_MASK, SN_IRQ_STAT, }; struct sn65dsi83_priv { struct i2c_client *client; struct device_node *disp_node; struct device_node *disp_dsi; struct gpio_desc *gp_en; struct clk *mipi_clk; struct notifier_block fbnb; struct notifier_block drmnb; u32 int_cnt; u32 pixelclock; u8 chip_enabled; u8 show_reg; u8 dsi_lanes; u8 spwg; /* lvds lane 3 has MSBs of color */ u8 jeida; /* lvds lane 3 has LSBs of color */ u8 dsi_bpp; u16 sync_delay; u8 dsi_clk_divider; u8 mipi_clk_index; }; /** * sn_i2c_read_reg - read data from a register of the i2c slave device. * * @client: i2c device. * @reg: the register to read from. * @buf: raw write data buffer. * @len: length of the buffer to write */ static int sn_i2c_read_byte(struct sn65dsi83_priv *sn, u8 reg) { struct i2c_client *client = sn->client; int ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) dev_err(&client->dev, "%s failed(%i)\n", __func__, ret); return ret; } /** * sn_i2c_write - write data to a register of the i2c slave device. * * @client: i2c device. * @reg: the register to write to. * @buf: raw data buffer to write. * @len: length of the buffer to write */ static int sn_i2c_write_byte(struct sn65dsi83_priv *sn, u8 reg, u8 val) { struct i2c_client *client = sn->client; int ret = i2c_smbus_write_byte_data(sn->client, reg, val); if (ret < 0) dev_err(&client->dev, "%s failed(%i)\n", __func__, ret); return ret; } static void sn_disable(struct sn65dsi83_priv *sn) { if (sn->chip_enabled) { disable_irq(sn->client->irq); sn->chip_enabled = 0; } gpiod_set_value(sn->gp_en, 0); } static void sn_enable_gp(struct gpio_desc *gp_en) { msleep(15); /* disabled for at least 10 ms */ gpiod_set_value(gp_en, 1); msleep(1); } static void sn_enable_irq(struct sn65dsi83_priv *sn) { sn_i2c_write_byte(sn, SN_IRQ_STAT, 0xff); sn_i2c_write_byte(sn, SN_IRQ_MASK, 0x7f); sn_i2c_write_byte(sn, SN_IRQ_EN, 1); } static int sn_get_dsi_clk_divider(struct sn65dsi83_priv *sn) { u32 dsi_clk_divider = 25; u32 mipi_clk_rate; u8 mipi_clk_index; int ret; u32 pixelclock = sn->pixelclock; mipi_clk_rate = clk_get_rate(sn->mipi_clk); if (!mipi_clk_rate) { pr_err("mipi clock is off\n"); /* Divided by 2 because mipi output clock is DDR */ mipi_clk_rate = pixelclock * sn->dsi_bpp / (sn->dsi_lanes * 2); } if (mipi_clk_rate > 500000000) { pr_err("mipi clock(%d) is too high\n", mipi_clk_rate); mipi_clk_rate = 500000000; } if (pixelclock) dsi_clk_divider = mipi_clk_rate / pixelclock; if (dsi_clk_divider > 25) dsi_clk_divider = 25; else if (!dsi_clk_divider) dsi_clk_divider = 1; mipi_clk_index = mipi_clk_rate / 5000000; if (mipi_clk_index < 8) mipi_clk_index = 8; ret = (sn->dsi_clk_divider == dsi_clk_divider) && (sn->mipi_clk_index == mipi_clk_index); if (!ret) pr_info("dsi_clk_divider = %d, mipi_clk_index=%d, mipi_clk_rate=%d\n", dsi_clk_divider, mipi_clk_index, mipi_clk_rate); sn->dsi_clk_divider = dsi_clk_divider; sn->mipi_clk_index = mipi_clk_index; return ret; } static int sn_setup_regs(struct sn65dsi83_priv *sn) { unsigned i = 5; int format = 0x10; u32 pixelclock; struct videomode vm; int ret; ret = of_get_videomode(sn->disp_dsi, &vm, 0); if (ret < 0) return ret; pixelclock = vm.pixelclock; if (pixelclock) { if (pixelclock > 37500000) { i = (pixelclock - 12500000) / 25000000; if (i > 5) i = 5; } } sn->pixelclock = pixelclock; pr_info("pixelclock=%d %dx%d, margins=%d,%d %d,%d syncs=%d %d\n", pixelclock, vm.hactive, vm.vactive, vm.hback_porch, vm.hfront_porch, vm.vback_porch, vm.vfront_porch, vm.hsync_len, vm.vsync_len); sn_i2c_write_byte(sn, SN_CLK_SRC, (i << 1) | 1); sn_get_dsi_clk_divider(sn); sn_i2c_write_byte(sn, SN_CLK_DIV, (sn->dsi_clk_divider - 1) << 3); sn_i2c_write_byte(sn, SN_DSI_LANES, ((4 - sn->dsi_lanes) << 3) | 0x20); sn_i2c_write_byte(sn, SN_DSI_EQ, 0); sn_i2c_write_byte(sn, SN_DSI_CLK, sn->mipi_clk_index); if (vm.flags & DISPLAY_FLAGS_DE_LOW) format |= BIT(7); if (!(vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)) format |= BIT(6); if (!(vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)) format |= BIT(5); if (sn->dsi_bpp == 24) { if (sn->spwg) { /* lvds lane 3 has MSBs of color */ format |= BIT(3); } else if (sn->jeida) { /* lvds lane 3 has LSBs of color */ format |= BIT(3) | BIT(1); } else { /* unused lvds lane 3 has LSBs of color */ format |= BIT(1); } } sn_i2c_write_byte(sn, SN_FORMAT, format); sn_i2c_write_byte(sn, SN_LVDS_VOLTAGE, 5); sn_i2c_write_byte(sn, SN_LVDS_TERM, 3); sn_i2c_write_byte(sn, SN_LVDS_CM_VOLTAGE, 0); sn_i2c_write_byte(sn, SN_HACTIVE_LOW, (u8)vm.hactive); sn_i2c_write_byte(sn, SN_HACTIVE_HIGH, (u8)(vm.hactive >> 8)); sn_i2c_write_byte(sn, SN_VACTIVE_LOW, (u8)vm.vactive); sn_i2c_write_byte(sn, SN_VACTIVE_HIGH, (u8)(vm.vactive >> 8)); sn_i2c_write_byte(sn, SN_SYNC_DELAY_LOW, (u8)sn->sync_delay); sn_i2c_write_byte(sn, SN_SYNC_DELAY_HIGH, (u8)(sn->sync_delay >> 8)); sn_i2c_write_byte(sn, SN_HSYNC_LOW, (u8)vm.hsync_len); sn_i2c_write_byte(sn, SN_HSYNC_HIGH, (u8)(vm.hsync_len >> 8)); sn_i2c_write_byte(sn, SN_VSYNC_LOW, (u8)vm.vsync_len); sn_i2c_write_byte(sn, SN_VSYNC_HIGH, (u8)(vm.vsync_len >> 8)); sn_i2c_write_byte(sn, SN_HBP, (u8)vm.hback_porch); sn_i2c_write_byte(sn, SN_VBP, (u8)vm.vback_porch); sn_i2c_write_byte(sn, SN_HFP, (u8)vm.hfront_porch); sn_i2c_write_byte(sn, SN_VFP, (u8)vm.vfront_porch); sn_i2c_write_byte(sn, SN_TEST_PATTERN, 0); return 0; } static void sn_enable_pll(struct sn65dsi83_priv *sn) { if (!sn_get_dsi_clk_divider(sn)) { sn_i2c_write_byte(sn, SN_CLK_DIV, (sn->dsi_clk_divider - 1) << 3); sn_i2c_write_byte(sn, SN_DSI_CLK, sn->mipi_clk_index); } sn_i2c_write_byte(sn, SN_PLL_EN, 1); msleep(5); sn_i2c_write_byte(sn, SN_SOFT_RESET, 1); sn_enable_irq(sn); } static void sn_disable_pll(struct sn65dsi83_priv *sn) { if (sn->chip_enabled) sn_i2c_write_byte(sn, SN_PLL_EN, 0); } static void sn_init(struct sn65dsi83_priv *sn) { sn_i2c_write_byte(sn, SN_SOFT_RESET, 1); sn_i2c_write_byte(sn, SN_PLL_EN, 0); sn_i2c_write_byte(sn, SN_IRQ_MASK, 0x0); sn_i2c_write_byte(sn, SN_IRQ_EN, 1); } static void sn_prepare(struct sn65dsi83_priv *sn) { sn_enable_gp(sn->gp_en); sn_init(sn); if (!sn->chip_enabled) { sn->chip_enabled = 1; enable_irq(sn->client->irq); } sn_setup_regs(sn); } static int sn_drm_event(struct notifier_block *nb, unsigned long event, void *data) { struct drm_device *drm_dev = data; struct device_node *node = drm_dev->dev->of_node; struct sn65dsi83_priv *sn = container_of(nb, struct sn65dsi83_priv, drmnb); struct device *dev = &sn->client->dev; dev_dbg(dev, "%s: event %lx\n", __func__, event); if (node != sn->disp_node) return 0; switch (event) { case DRM_MODE_DPMS_ON: sn_enable_pll(sn); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: sn_disable_pll(sn); break; default: dev_info(dev, "%s: unknown event %lx\n", __func__, event); } return 0; } static int sn_fb_event(struct notifier_block *nb, unsigned long event, void *data) { struct fb_event *evdata = data; struct fb_info *info = evdata->info; struct device_node *node = info->device->of_node; struct sn65dsi83_priv *sn = container_of(nb, struct sn65dsi83_priv, fbnb); struct device *dev; int blank_type; dev = &sn->client->dev; dev_dbg(dev, "%s: event %lx\n", __func__, event); if (node != sn->disp_node) return 0; switch (event) { case FB_R_EARLY_EVENT_BLANK: blank_type = *((int *)evdata->data); if (blank_type == FB_BLANK_UNBLANK) { sn_disable(sn); } else { sn_enable_pll(sn); } break; case FB_EARLY_EVENT_BLANK: blank_type = *((int *)evdata->data); if (blank_type == FB_BLANK_UNBLANK) { sn_prepare(sn); } else { sn_disable_pll(sn); } break; case FB_EVENT_BLANK: { blank_type = *((int *)evdata->data); if (blank_type == FB_BLANK_UNBLANK) { sn_enable_pll(sn); } else { sn_disable(sn); } dev_info(dev, "%s: blank type 0x%x\n", __func__, blank_type ); break; } case FB_EVENT_SUSPEND : { dev_info(dev, "%s: suspend\n", __func__ ); sn_disable(sn); break; } case FB_EVENT_RESUME : { dev_info(dev, "%s: resume\n", __func__ ); break; } case FB_EVENT_FB_REGISTERED : { if (clk_get_rate(sn->mipi_clk)) { sn_prepare(sn); sn_enable_pll(sn); } break; } default: dev_info(dev, "%s: unknown event %lx\n", __func__, event); } return 0; } /* * We only report errors in this handler */ static irqreturn_t sn_irq_handler(int irq, void *id) { struct sn65dsi83_priv *sn = id; int status = sn_i2c_read_byte(sn, SN_IRQ_STAT); if (status > 0) { sn_i2c_write_byte(sn, SN_IRQ_STAT, status); dev_info(&sn->client->dev, "%s: status %x %x %x\n", __func__, status, sn_i2c_read_byte(sn, SN_CLK_SRC), sn_i2c_read_byte(sn, SN_IRQ_MASK)); // if (status & 1) // sn_i2c_write_byte(sn, SN_SOFT_RESET, 1); if (sn->int_cnt > 10) { disable_irq_nosync(sn->client->irq); } else { msleep(100); } return IRQ_HANDLED; } else { dev_err(&sn->client->dev, "%s: read error %d\n", __func__, status); } return IRQ_NONE; } static ssize_t sn65dsi83_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sn65dsi83_priv *sn = dev_get_drvdata(dev); int val; const unsigned char *p = registers_to_show; int i = 0; int total = 0; int reg; int cnt; if (!sn->chip_enabled) return -EBUSY; if (sn->show_reg != 0) { val = sn_i2c_read_byte(sn, sn->show_reg); return sprintf(buf, "%02x: %02x\n", sn->show_reg, val); } while (i < ARRAY_SIZE(registers_to_show)) { reg = *p ; i ; if (!reg) { reg = *p ; i ; val = sn_i2c_read_byte(sn, reg); val |= sn_i2c_read_byte(sn, reg 1) << 8; cnt = sprintf(&buf[total], "%02x: %04x (%d)\n", reg, val, val); } else { val = sn_i2c_read_byte(sn, reg); cnt = sprintf(&buf[total], "%02x: %02x (%d)\n", reg, val, val); } if (cnt <= 0) break; total = cnt; } return total; } static ssize_t sn65dsi83_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned val; int ret; struct sn65dsi83_priv *sn = dev_get_drvdata(dev); char *endp; unsigned reg = simple_strtol(buf, &endp, 16); if (!sn->chip_enabled) return -EBUSY; if (reg > 0xe5) return count; sn->show_reg = reg; if (!endp) return count; if (*endp == 0x20) endp ; if (!*endp || *endp == 0x0a) return count; val = simple_strtol(endp, &endp, 16); if (val >= 0x100) return count; dev_err(dev, "%s:reg=0x%x, val=0x%x\n", __func__, reg, val); ret = sn_i2c_write_byte(sn, reg, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(sn65dsi83_reg, 0644, sn65dsi83_reg_show, sn65dsi83_reg_store); /* * I2C init/probing/exit functions */ static int sn65dsi83_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; struct sn65dsi83_priv *sn; struct i2c_adapter *adapter; struct device_node *np = client->dev.of_node; struct gpio_desc *gp_en; const char *df; u32 sync_delay; u32 dsi_lanes; dev_info(&client->dev, "sn65dsi83_probe 1\n"); adapter = to_i2c_adapter(client->dev.parent); ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA); if (!ret) { dev_err(&client->dev, "i2c_check_functionality failed\n"); return -ENODEV; } gp_en = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(gp_en)) { if (PTR_ERR(gp_en) != -EPROBE_DEFER) dev_err(&client->dev, "Failed to get enable gpio: %ld\n", PTR_ERR(gp_en)); return PTR_ERR(gp_en); } if (gp_en) { sn_enable_gp(gp_en); } else { dev_warn(&client->dev, "no enable pin available"); } ret = i2c_smbus_read_byte_data(client, SN_CLK_SRC); if (ret < 0) { /* enable might be used for something else, change to input */ gpiod_direction_input(gp_en); dev_info(&client->dev, "i2c read failed\n"); return -ENODEV; } // gpiod_set_value(gp_en, 0); sn = devm_kzalloc(&client->dev, sizeof(*sn), GFP_KERNEL); if (!sn) return -ENOMEM; sn->client = client; sn->gp_en = gp_en; sn_init(sn); sn->disp_dsi = of_parse_phandle(np, "display-dsi", 0); if (!sn->disp_dsi) return -ENODEV; sn->disp_node = of_parse_phandle(np, "display", 0); if (!sn->disp_node) return -ENODEV; sn->sync_delay = 0x120; if (!of_property_read_u32(np, "sync-delay", &sync_delay)) { if (sync_delay > 0xfff) return -EINVAL; sn->sync_delay = sync_delay; } if (of_property_read_u32(sn->disp_dsi, "dsi-lanes", &dsi_lanes) < 0) return -EINVAL; if (dsi_lanes < 1 || dsi_lanes > 4) return -EINVAL; sn->dsi_lanes = dsi_lanes; sn->spwg = of_property_read_bool(sn->disp_dsi, "spwg"); sn->jeida = of_property_read_bool(sn->disp_dsi, "jeida"); ret = of_property_read_string(sn->disp_dsi, "dsi-format", &df); if (ret) { dev_err(&client->dev, "dsi-format missing in display node%d\n", ret); return ret; } sn->dsi_bpp = !strcmp(df, "rgb666") ? 18 : 24; sn->mipi_clk = devm_clk_get(&client->dev, "mipi_clk"); if (IS_ERR(sn->mipi_clk)) return PTR_ERR(sn->mipi_clk); ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, sn_irq_handler, IRQF_ONESHOT, client->name, sn); if (ret) pr_info("%s: request_irq failed, irq:%i\n", client_name, client->irq); disable_irq(client->irq); i2c_set_clientdata(client, sn); sn->drmnb.notifier_call = sn_drm_event; ret = drm_register_client(&sn->drmnb); if (ret < 0) { dev_err(&client->dev, "drm_register_client failed(%d)\n", ret); return ret; } sn->fbnb.notifier_call = sn_fb_event; ret = fb_register_client(&sn->fbnb); if (ret < 0) { dev_err(&client->dev, "fb_register_client failed(%d)\n", ret); return ret; } ret = device_create_file(&client->dev, &dev_attr_sn65dsi83_reg); if (ret < 0) pr_warn("failed to add sn65dsi83 sysfs files\n"); sn_prepare(sn); dev_info(&client->dev, "succeeded\n"); return 0; } static int sn65dsi83_remove(struct i2c_client *client) { struct sn65dsi83_priv *sn = i2c_get_clientdata(client); device_remove_file(&client->dev, &dev_attr_sn65dsi83_reg); fb_unregister_client(&sn->drmnb); fb_unregister_client(&sn->fbnb); sn_disable(sn); return 0; } static const struct i2c_device_id sn65dsi83_id[] = { {"sn65dsi83", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, sn65dsi83_id); static struct i2c_driver sn65dsi83_driver = { .driver = { .name = "sn65dsi83", .owner = THIS_MODULE, }, .probe = sn65dsi83_probe, .remove = sn65dsi83_remove, .id_table = sn65dsi83_id, }; module_i2c_driver(sn65dsi83_driver); MODULE_AUTHOR("Boundary Devices, Inc."); MODULE_DESCRIPTION("sn65dsi83 mipi to lvds bridge"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/sn65dsi83/Kconfig b/drivers/gpu/drm/bridge/sn65dsi83/Kconfig new file mode 100644 index 000000000..1d8f37f68 --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/Kconfig @@ -0,0 1,7 @@ config DRM_I2C_SN65DSI83 bool "SN65DSI83 mipi dsi to lvds bridge" depends on OF select DRM_MIPI_DSI default y help Support for the sn65dsi83 MIPI DSI to LVDS bridge diff --git a/drivers/gpu/drm/bridge/sn65dsi83/Makefile b/drivers/gpu/drm/bridge/sn65dsi83/Makefile new file mode 100644 index 000000000..dee7f493b --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/Makefile @@ -0,0 1,2 @@ sn65dsi83-objs := sn65dsi83_drv.o sn65dsi83_brg.o obj-$(CONFIG_DRM_I2C_SN65DSI83) := sn65dsi83.o diff --git a/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.c b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.c new file mode 100644 index 000000000..20bbf2a54 --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.c @@ -0,0 1,400 @@ /* * Copyright (C) 2018 CopuLab Ltd. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/i2c.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/slab.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_connector.h> #include <video/mipi_display.h> #include <video/of_videomode.h> #include <video/videomode.h> #include "sn65dsi83_brg.h" /* Register addresses */ #define SN65DSI83_SOFT_RESET 0x09 #define SN65DSI83_CORE_PLL 0x0A #define LVDS_CLK_RANGE_SHIFT 1 #define HS_CLK_SRC_SHIFT 0 #define SN65DSI83_PLL_DIV 0x0B #define DSI_CLK_DIV_SHIFT 3 #define SN65DSI83_PLL_EN 0x0D #define SN65DSI83_DSI_CFG 0x10 #define CHA_DSI_LANES_SHIFT 3 #define SN65DSI83_DSI_EQ 0x11 #define SN65DSI83_CHA_DSI_CLK_RNG 0x12 #define SN65DSI83_CHB_DSI_CLK_RNG 0x13 #define SN65DSI83_LVDS_MODE 0x18 #define DE_NEG_POLARITY_SHIFT 7 #define HS_NEG_POLARITY_SHIFT 6 #define VS_NEG_POLARITY_SHIFT 5 #define LVDS_LINK_CFG_SHIFT 4 #define CHA_24BPP_MODE_SHIFT 3 #define CHA_24BPP_FMT1_SHIFT 1 #define SN65DSI83_LVDS_SIGN 0x19 #define SN65DSI83_LVDS_TERM 0x1A #define SN65DSI83_LVDS_CM_ADJ 0x1B #define SN65DSI83_CHA_LINE_LEN_LO 0x20 #define SN65DSI83_CHA_LINE_LEN_HI 0x21 #define SN65DSI83_CHB_LINE_LEN_LO 0x22 #define SN65DSI83_CHB_LINE_LEN_HI 0x23 #define SN65DSI83_CHA_VERT_LINES_LO 0x24 #define SN65DSI83_CHA_VERT_LINES_HI 0x25 #define SN65DSI83_CHB_VERT_LINES_LO 0x26 #define SN65DSI83_CHB_VERT_LINES_HI 0x27 #define SN65DSI83_CHA_SYNC_DELAY_LO 0x28 #define SN65DSI83_CHA_SYNC_DELAY_HI 0x29 #define SN65DSI83_CHB_SYNC_DELAY_LO 0x2A #define SN65DSI83_CHB_SYNC_DELAY_HI 0x2B #define SN65DSI83_CHA_HSYNC_WIDTH_LO 0x2C #define SN65DSI83_CHA_HSYNC_WIDTH_HI 0x2D #define SN65DSI83_CHB_HSYNC_WIDTH_LO 0x2E #define SN65DSI83_CHB_HSYNC_WIDTH_HI 0x2F #define SN65DSI83_CHA_VSYNC_WIDTH_LO 0x30 #define SN65DSI83_CHA_VSYNC_WIDTH_HI 0x31 #define SN65DSI83_CHB_VSYNC_WIDTH_LO 0x32 #define SN65DSI83_CHB_VSYNC_WIDTH_HI 0x33 #define SN65DSI83_CHA_HORZ_BACKPORCH 0x34 #define SN65DSI83_CHB_HORZ_BACKPORCH 0x35 #define SN65DSI83_CHA_VERT_BACKPORCH 0x36 #define SN65DSI83_CHB_VERT_BACKPORCH 0x37 #define SN65DSI83_CHA_HORZ_FRONTPORCH 0x38 #define SN65DSI83_CHB_HORZ_FRONTPORCH 0x39 #define SN65DSI83_CHA_VERT_FRONTPORCH 0x3A #define SN65DSI83_CHB_VERT_FRONTPORCH 0x3B #define SN65DSI83_CHA_ERR 0xE5 #define SN65DSI83_TEST_PATTERN 0x3C #define SN65DSI83_REG_3D 0x3D #define SN65DSI83_REG_3E 0x3E static int sn65dsi83_brg_power_on(struct sn65dsi83_brg *brg) { dev_info(&brg->client->dev,"%s\n",__func__); gpiod_set_value_cansleep(brg->gpio_enable, 1); /* Wait for 1ms for the internal voltage regulator to stabilize */ msleep(1); return 0; } static void sn65dsi83_brg_power_off(struct sn65dsi83_brg *brg) { dev_info(&brg->client->dev,"%s\n",__func__); gpiod_set_value_cansleep(brg->gpio_enable, 0); /* * The EN pin must be held low for at least 10 ms * before being asserted high */ msleep(10); } static int sn65dsi83_write(struct i2c_client *client, u8 reg, u8 val) { int ret; ret = i2c_smbus_write_byte_data(client, reg, val); if (ret) dev_err(&client->dev, "failed to write at 0x%02x", reg); dev_dbg(&client->dev, "%s: write reg 0x%02x data 0x%02x", __func__, reg, val); return ret; } #define SN65DSI83_WRITE(reg,val) sn65dsi83_write(client, (reg) , (val)) static int sn65dsi83_read(struct i2c_client *client, u8 reg) { int ret; dev_info(&client->dev, "client 0x%p", client); ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) { dev_err(&client->dev, "failed reading at 0x%02x", reg); return ret; } dev_dbg(&client->dev, "%s: read reg 0x%02x data 0x%02x", __func__, reg, ret); return ret; } #define SN65DSI83_READ(reg) sn65dsi83_read(client, (reg)) static int sn65dsi83_brg_start_stream(struct sn65dsi83_brg *brg) { int regval; struct i2c_client *client = I2C_CLIENT(brg); dev_info(&client->dev,"%s\n",__func__); /* Set the PLL_EN bit (CSR 0x0D.0) */ SN65DSI83_WRITE(SN65DSI83_PLL_EN, 0x1); /* Wait for the PLL_LOCK bit to be set (CSR 0x0A.7) */ msleep(200); /* Perform SW reset to apply changes */ SN65DSI83_WRITE(SN65DSI83_SOFT_RESET, 0x01); /* Read CHA Error register */ regval = SN65DSI83_READ(SN65DSI83_CHA_ERR); dev_info(&client->dev, "CHA (0x%02x) = 0x%02x", SN65DSI83_CHA_ERR, regval); SN65DSI83_WRITE(SN65DSI83_CHA_ERR, 0xff); regval = SN65DSI83_READ(SN65DSI83_CHA_ERR); dev_info(&client->dev, "CHA (0x%02x) = 0x%02x", SN65DSI83_CHA_ERR, regval); msleep(10); regval = SN65DSI83_READ(SN65DSI83_CHA_ERR); dev_info(&client->dev, "CHA (0x%02x) = 0x%02x", SN65DSI83_CHA_ERR, regval); return 0; } static void sn65dsi83_brg_stop_stream(struct sn65dsi83_brg *brg) { struct i2c_client *client = I2C_CLIENT(brg); dev_info(&client->dev,"%s\n",__func__); /* Clear the PLL_EN bit (CSR 0x0D.0) */ SN65DSI83_WRITE(SN65DSI83_PLL_EN, 0x00); } static int sn65dsi83_calk_clk_range(int min_regval, int max_regval, unsigned long min_clk, unsigned long inc, unsigned long target_clk) { int regval = min_regval; unsigned long clk = min_clk; while (regval <= max_regval) { if ((clk <= target_clk) && (target_clk < (clk inc))) return regval; regval ; clk = inc; } return -1; } #define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) static int sn65dsi83_calk_div(int min_regval, int max_regval, int min_div, int inc, unsigned long source_clk, unsigned long target_clk) { int regval = min_regval; int div = min_div; unsigned long curr_delta; unsigned long prev_delta = ABS(DIV_ROUND_UP(source_clk, div) - target_clk); while (regval <= max_regval) { curr_delta = ABS(DIV_ROUND_UP(source_clk, div) - target_clk); if (curr_delta > prev_delta) return --regval; regval ; div = inc; } return -1; } static int sn65dsi83_brg_configure(struct sn65dsi83_brg *brg) { int regval = 0; struct i2c_client *client = I2C_CLIENT(brg); struct videomode *vm = VM(brg); u32 dsi_clk = (((PIXCLK * BPP(brg)) / DSI_LANES(brg)) >> 1); dev_info(&client->dev, "DSI clock [ %u ] Hz\n",dsi_clk); dev_info(&client->dev, "GeoMetry [ %d x %d ] Hz\n",HACTIVE,VACTIVE); /* Reset PLL_EN and SOFT_RESET registers */ SN65DSI83_WRITE(SN65DSI83_SOFT_RESET,0x00); SN65DSI83_WRITE(SN65DSI83_PLL_EN,0x00); /* LVDS clock setup */ if ((25000000 <= PIXCLK) && (PIXCLK < 37500000)) regval = 0; else regval = sn65dsi83_calk_clk_range(0x01, 0x05, 37500000, 25000000, PIXCLK); if (regval < 0) { dev_err(&client->dev, "failed to configure LVDS clock"); return -EINVAL; } regval = (regval << LVDS_CLK_RANGE_SHIFT); regval |= (1 << HS_CLK_SRC_SHIFT); /* Use DSI clock */ SN65DSI83_WRITE(SN65DSI83_CORE_PLL,regval); /* DSI clock range */ regval = sn65dsi83_calk_clk_range(0x08, 0x64, 40000000, 5000000, dsi_clk); if (regval < 0) { dev_err(&client->dev, "failed to configure DSI clock range\n"); return -EINVAL; } SN65DSI83_WRITE(SN65DSI83_CHA_DSI_CLK_RNG,regval); /* DSI clock divider */ regval = sn65dsi83_calk_div(0x0, 0x18, 1, 1, dsi_clk, PIXCLK); if (regval < 0) { dev_err(&client->dev, "failed to calculate DSI clock divider"); return -EINVAL; } regval = regval << DSI_CLK_DIV_SHIFT; SN65DSI83_WRITE(SN65DSI83_PLL_DIV,regval); /* Configure DSI_LANES */ regval = SN65DSI83_READ(SN65DSI83_DSI_CFG); regval &= ~(3 << CHA_DSI_LANES_SHIFT); regval |= ((4 - DSI_LANES(brg)) << CHA_DSI_LANES_SHIFT); printk("DSI lanes = %d.... val = 0x%x\n", (int)DSI_LANES(brg), regval); SN65DSI83_WRITE(SN65DSI83_DSI_CFG,regval); /* CHA_DSI_DATA_EQ - No Equalization */ /* CHA_DSI_CLK_EQ - No Equalization */ SN65DSI83_WRITE(SN65DSI83_DSI_EQ,0x00); /* Video formats */ regval = 0; if (FLAGS & DISPLAY_FLAGS_HSYNC_LOW) regval |= (1 << HS_NEG_POLARITY_SHIFT); if (FLAGS & DISPLAY_FLAGS_VSYNC_LOW) regval |= (1 << VS_NEG_POLARITY_SHIFT); if (FLAGS & DISPLAY_FLAGS_DE_LOW) regval |= (1 << DE_NEG_POLARITY_SHIFT); if (BPP(brg) == 24) regval |= (1 << CHA_24BPP_MODE_SHIFT); if (FORMAT(brg) == 1) regval |= (1 << CHA_24BPP_FMT1_SHIFT); regval |= (1 << LVDS_LINK_CFG_SHIFT); printk("SN65DSI83_LVDS_MODE = 0x%x\n", regval); SN65DSI83_WRITE(SN65DSI83_LVDS_MODE,regval); /* Voltage and pins */ SN65DSI83_WRITE(SN65DSI83_LVDS_SIGN,0x00); SN65DSI83_WRITE(SN65DSI83_LVDS_TERM,0x03); SN65DSI83_WRITE(SN65DSI83_LVDS_CM_ADJ,0x00); /* Configure sync delay to minimal allowed value */ SN65DSI83_WRITE(SN65DSI83_CHA_SYNC_DELAY_LO,0x21); SN65DSI83_WRITE(SN65DSI83_CHA_SYNC_DELAY_HI,0x00); /* Geometry */ SN65DSI83_WRITE(SN65DSI83_CHA_LINE_LEN_LO,LOW(HACTIVE)); SN65DSI83_WRITE(SN65DSI83_CHA_LINE_LEN_HI,HIGH(HACTIVE)); SN65DSI83_WRITE(SN65DSI83_CHA_VERT_LINES_LO,LOW(VACTIVE)); SN65DSI83_WRITE(SN65DSI83_CHA_VERT_LINES_HI,HIGH(VACTIVE)); SN65DSI83_WRITE(SN65DSI83_CHA_HSYNC_WIDTH_LO,LOW(HPW)); SN65DSI83_WRITE(SN65DSI83_CHA_HSYNC_WIDTH_HI,HIGH(HPW)); SN65DSI83_WRITE(SN65DSI83_CHA_VSYNC_WIDTH_LO,LOW(VPW)); SN65DSI83_WRITE(SN65DSI83_CHA_VSYNC_WIDTH_HI,HIGH(VPW)); SN65DSI83_WRITE(SN65DSI83_CHA_HORZ_BACKPORCH,LOW(HBP)); SN65DSI83_WRITE(SN65DSI83_CHA_VERT_BACKPORCH,LOW(VBP)); SN65DSI83_WRITE(SN65DSI83_CHA_HORZ_FRONTPORCH,LOW(HFP)); SN65DSI83_WRITE(SN65DSI83_CHA_VERT_FRONTPORCH,LOW(VFP)); SN65DSI83_WRITE(SN65DSI83_TEST_PATTERN,0x00); SN65DSI83_WRITE(SN65DSI83_REG_3D,0x00); SN65DSI83_WRITE(SN65DSI83_REG_3E,0x00); /* mute channel B */ SN65DSI83_WRITE(SN65DSI83_CHB_DSI_CLK_RNG, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_LINE_LEN_LO, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_LINE_LEN_HI, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VERT_LINES_LO, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VERT_LINES_HI, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_SYNC_DELAY_LO, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_SYNC_DELAY_HI, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_HSYNC_WIDTH_LO, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_HSYNC_WIDTH_HI, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VSYNC_WIDTH_LO, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VSYNC_WIDTH_HI, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_HORZ_BACKPORCH, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VERT_BACKPORCH, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_HORZ_FRONTPORCH, 0x00); SN65DSI83_WRITE(SN65DSI83_CHB_VERT_FRONTPORCH, 0x00); return 0; } static int sn65dsi83_brg_setup(struct sn65dsi83_brg *brg) { struct i2c_client *client = I2C_CLIENT(brg); dev_info(&client->dev,"%s\n",__func__); sn65dsi83_brg_power_on(brg); return sn65dsi83_brg_configure(brg); } static int sn65dsi83_brg_reset(struct sn65dsi83_brg *brg) { /* Soft Reset reg value at power on should be 0x00 */ struct i2c_client *client = I2C_CLIENT(brg); int ret = SN65DSI83_READ(SN65DSI83_SOFT_RESET); dev_info(&client->dev,"%s\n",__func__); if (ret != 0x00) { dev_err(&client->dev,"Failed to reset the device"); return -ENODEV; } return 0; } static struct sn65dsi83_brg_funcs brg_func = { .power_on = sn65dsi83_brg_power_on, .power_off = sn65dsi83_brg_power_off, .setup = sn65dsi83_brg_setup, .reset = sn65dsi83_brg_reset, .start_stream = sn65dsi83_brg_start_stream, .stop_stream = sn65dsi83_brg_stop_stream, }; static struct sn65dsi83_brg brg = { .funcs = &brg_func, }; struct sn65dsi83_brg *sn65dsi83_brg_get(void) { return &brg; } diff --git a/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.h b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.h new file mode 100644 index 000000000..9f23df8af --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_brg.h @@ -0,0 1,55 @@ #ifndef _SN65DSI83_BRG_H__ #define _SN65DSI83_BRG_H__ #include <linux/i2c.h> #include <linux/gpio/consumer.h> #include <video/videomode.h> struct sn65dsi83_brg; struct sn65dsi83_brg_funcs { int (*power_on)(struct sn65dsi83_brg *sn65dsi8383_brg); void (*power_off)(struct sn65dsi83_brg *sn65dsi8383_brg); int (*reset)(struct sn65dsi83_brg *sn65dsi8383_brg); int (*setup)(struct sn65dsi83_brg *sn65dsi8383_brg); int (*start_stream)(struct sn65dsi83_brg *sn65dsi8383_brg); void (*stop_stream)(struct sn65dsi83_brg *sn65dsi8383_brg); }; struct sn65dsi83_brg { struct i2c_client *client; struct gpio_desc *gpio_enable; /* Bridge Panel Parameters */ struct videomode vm; u32 width_mm; u32 height_mm; u32 format; u32 bpp; u8 num_dsi_lanes; struct sn65dsi83_brg_funcs *funcs; }; struct sn65dsi83_brg *sn65dsi83_brg_get(void); #define I2C_DEVICE(A) &(A)->client->dev #define I2C_CLIENT(A) (A)->client #define VM(A) &(A)->vm #define BPP(A) (A)->bpp #define FORMAT(A) (A)->format #define DSI_LANES(A) (A)->num_dsi_lanes /* The caller has to have a vm structure defined */ #define PIXCLK vm->pixelclock #define HACTIVE vm->hactive #define HFP vm->hfront_porch #define HBP vm->hback_porch #define HPW vm->hsync_len #define VACTIVE vm->vactive #define VFP vm->vfront_porch #define VBP vm->vback_porch #define VPW vm->vsync_len #define FLAGS vm->flags #define HIGH(A) (((A) >> 8) & 0xFF) #define LOW(A) ((A) & 0xFF) #endif /* _SN65DSI83_BRG_H__ */ diff --git a/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_drv.c b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_drv.c new file mode 100644 index 000000000..b0adb20de --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_drv.c @@ -0,0 1,433 @@ /* * Licensed under the GPL-2. */ #define DEBUG #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/slab.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_connector.h> #include <drm/drm_crtc_helper.h> #include <video/mipi_display.h> #include <video/of_videomode.h> #include <video/videomode.h> #include "sn65dsi83_timing.h" #include "sn65dsi83_brg.h" struct sn65dsi83 { u8 channel_id; enum drm_connector_status status; bool powered; struct drm_display_mode curr_mode; struct drm_bridge bridge; struct drm_connector connector; struct device_node *host_node; struct mipi_dsi_device *dsi; struct sn65dsi83_brg *brg; }; static int sn65dsi83_attach_dsi(struct sn65dsi83 *sn65dsi83); #define DRM_DEVICE(A) A->dev->dev /* Connector funcs */ static struct sn65dsi83 *connector_to_sn65dsi83(struct drm_connector *connector) { return container_of(connector, struct sn65dsi83, connector); } static int sn65dsi83_connector_get_modes(struct drm_connector *connector) { struct sn65dsi83 *sn65dsi83 = connector_to_sn65dsi83(connector); struct sn65dsi83_brg *brg = sn65dsi83->brg; struct device *dev = connector->dev->dev; struct drm_display_mode *mode; u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; u32 *bus_flags = &connector->display_info.bus_flags; int ret; dev_info(dev, "%s\n",__func__); mode = drm_mode_create(connector->dev); if (!mode) { DRM_DEV_ERROR(dev, "Failed to create display mode!\n"); return 0; } drm_display_mode_from_videomode(&brg->vm, mode); mode->width_mm = brg->width_mm; mode->height_mm = brg->height_mm; mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); drm_mode_connector_list_update(connector); connector->display_info.width_mm = mode->width_mm; connector->display_info.height_mm = mode->height_mm; if (brg->vm.flags & DISPLAY_FLAGS_DE_HIGH) *bus_flags |= DRM_BUS_FLAG_DE_HIGH; if (brg->vm.flags & DISPLAY_FLAGS_DE_LOW) *bus_flags |= DRM_BUS_FLAG_DE_LOW; if (brg->vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE; if (brg->vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) *bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE; ret = drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1); if (ret) return ret; return 1; } static enum drm_mode_status sn65dsi83_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct sn65dsi83 *sn65dsi83 = connector_to_sn65dsi83(connector); struct device *dev = connector->dev->dev; if (mode->clock > ( sn65dsi83->brg->vm.pixelclock / 1000 )) return MODE_CLOCK_HIGH; dev_info(dev, "%s: mode: %d*%d@%d is valid\n",__func__, mode->hdisplay,mode->vdisplay,mode->clock); //drm_kms_helper_hotplug_event(connector->dev); return MODE_OK; } static struct drm_connector_helper_funcs sn65dsi83_connector_helper_funcs = { .get_modes = sn65dsi83_connector_get_modes, .mode_valid = sn65dsi83_connector_mode_valid, }; static enum drm_connector_status sn65dsi83_connector_detect(struct drm_connector *connector, bool force) { struct sn65dsi83 *sn65dsi83 = connector_to_sn65dsi83(connector); struct device *dev = connector->dev->dev; enum drm_connector_status status; dev_info(dev, "%s\n",__func__); status = connector_status_connected; sn65dsi83->status = status; return status; } static int sn65dsi83_drm_helper_connector_dpms(struct drm_connector *connector, int mode) { drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); return 0; } static struct drm_connector_funcs sn65dsi83_connector_funcs = { // .dpms = drm_atomic_helper_connector_dpms, .dpms = sn65dsi83_drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sn65dsi83_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, }; /* Bridge funcs */ static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge) { return container_of(bridge, struct sn65dsi83, bridge); } static void sn65dsi83_bridge_enable(struct drm_bridge *bridge) { struct sn65dsi83 *sn65dsi83 = bridge_to_sn65dsi83(bridge); dev_info(DRM_DEVICE(bridge),"%s\n",__func__); sn65dsi83->brg->funcs->setup(sn65dsi83->brg); sn65dsi83->brg->funcs->start_stream(sn65dsi83->brg); } static void sn65dsi83_bridge_disable(struct drm_bridge *bridge) { struct sn65dsi83 *sn65dsi83 = bridge_to_sn65dsi83(bridge); dev_info(DRM_DEVICE(bridge),"%s\n",__func__); sn65dsi83->brg->funcs->stop_stream(sn65dsi83->brg); sn65dsi83->brg->funcs->power_off(sn65dsi83->brg); } static void sn65dsi83_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) { struct sn65dsi83 *sn65dsi83 = bridge_to_sn65dsi83(bridge); dev_info(DRM_DEVICE(bridge), "%s: mode: %d*%d@%d\n",__func__, mode->hdisplay,mode->vdisplay,mode->clock); drm_mode_copy(&sn65dsi83->curr_mode, adj_mode); } static int sn65dsi83_bridge_attach(struct drm_bridge *bridge) { struct sn65dsi83 *sn65dsi83 = bridge_to_sn65dsi83(bridge); int ret; dev_info(DRM_DEVICE(bridge),"%s\n",__func__); if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; } sn65dsi83->connector.polled = DRM_CONNECTOR_POLL_CONNECT; ret = drm_connector_init(bridge->dev, &sn65dsi83->connector, &sn65dsi83_connector_funcs, DRM_MODE_CONNECTOR_DSI); if (ret) { DRM_ERROR("Failed to initialize connector with drm\n"); return ret; } drm_connector_helper_add(&sn65dsi83->connector, &sn65dsi83_connector_helper_funcs); drm_mode_connector_attach_encoder(&sn65dsi83->connector, bridge->encoder); ret = sn65dsi83_attach_dsi(sn65dsi83); // MM //drm_bridge_enable(bridge); return ret; } static struct drm_bridge_funcs sn65dsi83_bridge_funcs = { .enable = sn65dsi83_bridge_enable, .disable = sn65dsi83_bridge_disable, .mode_set = sn65dsi83_bridge_mode_set, .attach = sn65dsi83_bridge_attach, }; static int sn65dsi83_parse_dt(struct device_node *np, struct sn65dsi83 *sn65dsi83) { struct device *dev = &sn65dsi83->brg->client->dev; u32 num_lanes = 4, bpp = 24, format = 2, width = 149, height = 93; struct device_node *endpoint; dev_dbg(dev, "sn65dsi83_parse_dt\n"); endpoint = of_graph_get_next_endpoint(np, NULL); if (!endpoint) return -ENODEV; sn65dsi83->host_node = of_graph_get_remote_port_parent(endpoint); if (!sn65dsi83->host_node) { of_node_put(endpoint); return -ENODEV; } dev_dbg(dev, "sn65dsi83_parse_dt... parsing dt\n"); of_property_read_u32(np, "ti,dsi-lanes", &num_lanes); of_property_read_u32(np, "ti,lvds-format", &format); of_property_read_u32(np, "ti,lvds-bpp", &bpp); of_property_read_u32(np, "ti,width-mm", &width); of_property_read_u32(np, "ti,height-mm", &height); if (num_lanes < 1 || num_lanes > 4) { dev_err(dev, "Invalid dsi-lanes: %d\n", num_lanes); return -EINVAL; } sn65dsi83->brg->num_dsi_lanes = num_lanes; sn65dsi83->brg->gpio_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(sn65dsi83->brg->gpio_enable)) { dev_err(dev, "failed to parse enable gpio"); return PTR_ERR(sn65dsi83->brg->gpio_enable); } sn65dsi83->brg->format = format; sn65dsi83->brg->bpp = bpp; sn65dsi83->brg->width_mm = width; sn65dsi83->brg->height_mm = height; /* Read default timing if there is not device tree node for */ if ((of_get_videomode(np, &sn65dsi83->brg->vm, 0)) < 0) { dev_dbg(dev, "******** panel_default_timing\n"); videomode_from_timing(&panel_default_timing, &sn65dsi83->brg->vm); } of_node_put(endpoint); of_node_put(sn65dsi83->host_node); return 0; } static int sn65dsi83_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct sn65dsi83 *sn65dsi83; struct device *dev = &i2c->dev; int ret; dev_info(dev,"%s\n",__func__); if (!dev->of_node) return -EINVAL; sn65dsi83 = devm_kzalloc(dev, sizeof(*sn65dsi83), GFP_KERNEL); if (!sn65dsi83) return -ENOMEM; /* Initialize it before DT parser */ sn65dsi83->brg = sn65dsi83_brg_get(); sn65dsi83->brg->client = i2c; sn65dsi83->powered = false; sn65dsi83->status = connector_status_disconnected; i2c_set_clientdata(i2c, sn65dsi83); ret = sn65dsi83_parse_dt(dev->of_node, sn65dsi83); if (ret) return ret; sn65dsi83->brg->funcs->power_off(sn65dsi83->brg); sn65dsi83->brg->funcs->power_on(sn65dsi83->brg); ret = sn65dsi83->brg->funcs->reset(sn65dsi83->brg); if (ret != 0x00) { dev_err(dev, "Failed to reset the device"); return -ENODEV; } sn65dsi83->brg->funcs->power_off(sn65dsi83->brg); sn65dsi83->bridge.funcs = &sn65dsi83_bridge_funcs; sn65dsi83->bridge.of_node = dev->of_node; ret = drm_bridge_add(&sn65dsi83->bridge); if (ret) { dev_err(dev, "failed to add sn65dsi83 bridge\n"); } dev_info(dev,"%s done\n",__func__); return ret; } static int sn65dsi83_attach_dsi(struct sn65dsi83 *sn65dsi83) { struct device *dev = &sn65dsi83->brg->client->dev; struct mipi_dsi_host *host; struct mipi_dsi_device *dsi; int ret = 0; const struct mipi_dsi_device_info info = { .type = "sn65dsi83", .channel = 0, .node = NULL, }; dev_info(dev, "%s\n",__func__); host = of_find_mipi_dsi_host_by_node(sn65dsi83->host_node); if (!host) { dev_err(dev, "failed to find dsi host\n"); return -EPROBE_DEFER; } dsi = mipi_dsi_device_register_full(host, &info); if (IS_ERR(dsi)) { dev_err(dev, "failed to create dsi device\n"); ret = PTR_ERR(dsi); return -ENODEV; } sn65dsi83->dsi = dsi; dsi->lanes = sn65dsi83->brg->num_dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; // dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE | // MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_VIDEO_BURST; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE; // dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | // MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; ret = mipi_dsi_attach(dsi); if (ret < 0) { dev_err(dev, "failed to attach dsi to host\n"); mipi_dsi_device_unregister(dsi); } return ret; } static void sn65dsi83_detach_dsi(struct sn65dsi83 *sn65dsi83) { struct device *dev = &sn65dsi83->brg->client->dev; dev_info(dev, "%s\n",__func__); mipi_dsi_detach(sn65dsi83->dsi); mipi_dsi_device_unregister(sn65dsi83->dsi); } static int sn65dsi83_remove(struct i2c_client *i2c) { struct sn65dsi83 *sn65dsi83 = i2c_get_clientdata(i2c); struct device *dev = &sn65dsi83->brg->client->dev; dev_info(dev, "%s\n",__func__); sn65dsi83_detach_dsi(sn65dsi83); drm_bridge_remove(&sn65dsi83->bridge); return 0; } static const struct i2c_device_id sn65dsi83_i2c_ids[] = { { "sn65dsi83", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, sn65dsi83_i2c_ids); static const struct of_device_id sn65dsi83_of_ids[] = { { .compatible = "ti,sn65dsi83" }, { } }; MODULE_DEVICE_TABLE(of, sn65dsi83_of_ids); static struct mipi_dsi_driver sn65dsi83_dsi_driver = { .driver.name = "sn65dsi83", }; static struct i2c_driver sn65dsi83_driver = { .driver = { .name = "sn65dsi83", .of_match_table = sn65dsi83_of_ids, }, .id_table = sn65dsi83_i2c_ids, .probe = sn65dsi83_probe, .remove = sn65dsi83_remove, }; static int __init sn65dsi83_init(void) { if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) mipi_dsi_driver_register(&sn65dsi83_dsi_driver); return i2c_add_driver(&sn65dsi83_driver); } module_init(sn65dsi83_init); static void __exit sn65dsi83_exit(void) { i2c_del_driver(&sn65dsi83_driver); if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) mipi_dsi_driver_unregister(&sn65dsi83_dsi_driver); } module_exit(sn65dsi83_exit); MODULE_AUTHOR("CompuLab <compulab@compula.co.il>"); MODULE_DESCRIPTION("SN65DSI bridge driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_timing.h b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_timing.h new file mode 100644 index 000000000..e9bb6633c --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi83/sn65dsi83_timing.h @@ -0,0 1,33 @@ #ifndef __SN65DSI83_TIMING_H__ #define __SN65DSI83_TIMING_H__ /* Default Video Parameters */ #define PIXCLK_INIT 62500000 #define HACTIVE_INIT 1280 #define HPW_INIT 2 #define HBP_INIT 6 #define HFP_INIT 5 #define VACTIVE_INIT 800 #define VPW_INIT 1 #define VBP_INIT 2 #define VFP_INIT 3 static const struct display_timing panel_default_timing = { .pixelclock = { PIXCLK_INIT, PIXCLK_INIT, PIXCLK_INIT }, .hactive = { HACTIVE_INIT, HACTIVE_INIT, HACTIVE_INIT }, .hfront_porch = { HFP_INIT, HFP_INIT, HFP_INIT }, .hsync_len = { HPW_INIT, HPW_INIT, HPW_INIT }, .hback_porch = { HBP_INIT, HBP_INIT, HBP_INIT }, .vactive = { VACTIVE_INIT, VACTIVE_INIT, VACTIVE_INIT }, .vfront_porch = { VFP_INIT, VFP_INIT, VFP_INIT }, .vsync_len = { VPW_INIT, VPW_INIT, VPW_INIT }, .vback_porch = { VBP_INIT, VBP_INIT, VBP_INIT }, .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_DE_LOW | DISPLAY_FLAGS_PIXDATA_NEGEDGE, }; #endif /* __SN65DSI83_TIMING_H__ */ diff --git a/drivers/gpu/drm/bridge/sn65dsi84-dsi2lvds.c b/drivers/gpu/drm/bridge/sn65dsi84-dsi2lvds.c new file mode 100644 index 000000000..2d0858505 --- /dev/null b/drivers/gpu/drm/bridge/sn65dsi84-dsi2lvds.c @@ -0,0 1,210 @@ /* * Texas Instruments sn65dsi84 DSI to LVDS bridge driver. * * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio/consumer.h> static int sn65dsi84_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) { int ret; if (writelen > 0) { struct i2c_msg msgs[] = { { .addr = client->addr, .flags = 0, .len = writelen, .buf = writebuf, }, { .addr = client->addr, .flags = I2C_M_RD, .len = readlen, .buf = readbuf, }, }; ret = i2c_transfer(client->adapter, msgs, 2); if (ret < 0) dev_err(&client->dev, "%s: i2c read error.\n", __func__); } else { struct i2c_msg msgs[] = { { .addr = client->addr, .flags = I2C_M_RD, .len = readlen, .buf = readbuf, }, }; ret = i2c_transfer(client->adapter, msgs, 1); if (ret < 0) dev_err(&client->dev, "%s:i2c read error.\n", __func__); } return ret; } static int sn65dsi84_i2c_write(struct i2c_client *client, char *writebuf, int writelen) { int ret; struct i2c_msg msgs[] = { { .addr = client->addr, .flags = 0, .len = writelen, .buf = writebuf, }, }; ret = i2c_transfer(client->adapter, msgs, 1); if (ret < 0) dev_err(&client->dev, "%s: i2c write error.\n", __func__); return ret; } static int sn65dsi84_write_reg(struct i2c_client *client, u8 addr, const u8 val) { u8 buf[2] = {0}; buf[0] = addr; buf[1] = val; return sn65dsi84_i2c_write(client, buf, sizeof(buf)); } static int sn65dsi84_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct property *prop; int err; int i, size; struct device_node *np = client->dev.of_node; int addresses[100]; int values[100]; int chipid[] = {0x35, 0x38, 0x49, 0x53, 0x44, 0x20, 0x20, 0x20, 0x01}; char address, value; struct gpio_desc *enable_gpio; enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); if (enable_gpio) gpiod_set_value_cansleep(enable_gpio, 1); for (i = 0; i < sizeof(chipid) / sizeof(int); i ) { address = (char)i; err = sn65dsi84_i2c_read(client, &address, 1, &value, 1); if (err < 0) { dev_err(&client->dev, "failed to read chip id\n"); return err; } if (value != chipid[i]) { dev_err(&client->dev, "chip id is not correct\n"); return err; } } prop = of_find_property(np, "sn65dsi84,addresses", NULL); if (!prop) return -EINVAL; if (!prop->value) return -ENODATA; size = prop->length / sizeof(int); err = of_property_read_u32_array(np, "sn65dsi84,addresses", addresses, size); if (err && (err != -EINVAL)) { dev_err(&client->dev, "Unable to read 'sn65dsi84,addresses'\n"); return err; } prop = of_find_property(np, "sn65dsi84,values", NULL); if (!prop) return -EINVAL; if (!prop->value) return -ENODATA; i = prop->length / sizeof(u32); if (i != size) { dev_err(&client->dev, "invalid 'sn65dsi84,values' length should be same as addresses\n"); return -EINVAL; } err = of_property_read_u32_array(np, "sn65dsi84,values", values, i); if (err && (err != -EINVAL)) { dev_err(&client->dev, "Unable to read 'sn65dsi84,values'\n"); return err; } for (i = 0; i < size; i ) { sn65dsi84_write_reg(client, addresses[i], values[i]); if (err < 0) { dev_err(&client->dev, "failed to write data to the chip\n"); return err; } } return 0; } static int sn65dsi84_remove(struct i2c_client *client) { struct gpio_desc *enable_gpio; enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_LOW); if (enable_gpio) gpiod_set_value_cansleep(enable_gpio, 0); return 0; } static const struct i2c_device_id sn65dsi84_id[] = { {"sn65dsi84", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, sn65dsi84_id); static struct of_device_id sn65dsi84_match_table[] = { { .compatible = "ti,sn65dsi84",}, { }, }; static struct i2c_driver sn65dsi84_i2c_driver = { .probe = sn65dsi84_probe, .remove = sn65dsi84_remove, .driver = { .name = "sn65dsi84", .owner = THIS_MODULE, .of_match_table = sn65dsi84_match_table, }, .id_table = sn65dsi84_id, }; static int __init sn65dsi84_init(void) { return i2c_add_driver(&sn65dsi84_i2c_driver); } static void __exit sn65dsi84_exit(void) { i2c_del_driver(&sn65dsi84_i2c_driver); } module_init(sn65dsi84_init); module_exit(sn65dsi84_exit); MODULE_DESCRIPTION("TI SN65DSI84 DSI to LVDS bridge driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 4a3f68a33..6d6ed9d2c 100644 --- a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c @@ -1340,7 1340,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, bool yres_specified = false, cvt = false, rb = false; bool interlace = false, margins = false, was_digit = false; int i; - enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; enum drm_connector_force force = DRM_FORCE_ON; #ifdef CONFIG_FB if (!mode_option) diff --git a/drivers/gpu/drm/drm_notify.c b/drivers/gpu/drm/drm_notify.c new file mode 100644 index 000000000..f5d46f375 --- /dev/null b/drivers/gpu/drm/drm_notify.c @@ -0,0 1,43 @@ // SPDX-License-Identifier: (GPL-2.0 ) /* * linux/drivers/gpu/drm/drm_notify.c * * Based on: * linux/drivers/video/fb_notify.c * * Copyright (C) 2018 Boundary Devices LLC */ #include <linux/notifier.h> #include <linux/export.h> static BLOCKING_NOTIFIER_HEAD(drm_notifier_list); /** * drm_register_client - register a client notifier * @nb: notifier block to callback on events */ int drm_register_client(struct notifier_block *nb) { return blocking_notifier_chain_register(&drm_notifier_list, nb); } EXPORT_SYMBOL(drm_register_client); /** * drm_unregister_client - unregister a client notifier * @nb: notifier block to callback on events */ int drm_unregister_client(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&drm_notifier_list, nb); } EXPORT_SYMBOL(drm_unregister_client); /** * drm_notifier_call_chain - notify clients of events * */ int drm_notifier_call_chain(unsigned long val, void *v) { return blocking_notifier_call_chain(&drm_notifier_list, val, v); } EXPORT_SYMBOL_GPL(drm_notifier_call_chain); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 59be1232d..8f1e7839f 100644 --- a/include/drm/drmP.h b/include/drm/drmP.h @@ -348,4 348,8 @@ static __inline__ bool drm_can_sleep(void) /* helper for handling conditionals in various for_each macros */ #define for_each_if(condition) if (!(condition)) {} else extern int drm_register_client(struct notifier_block *nb); extern int drm_unregister_client(struct notifier_block *nb); extern int drm_notifier_call_chain(unsigned long val, void *v); #endif