From nobody Thu Dec 18 10:37:43 2025 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FB4A1FA165; Tue, 11 Feb 2025 11:44:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739274279; cv=none; b=egWRmIVv/0XTlBcf9urmNGwR4wlRE6gVqRTxUV+acyULKdFCZgpIoc7dwNX2uir5SuVwFm+MfUmyDhyT4A+uwBVwI2mAPZWWo2Q7WkbcHYYlRypyLFR10KWj8ycUGIEKJNJ/K+5nad4j7qH6AWgoG7I5LKUuNX2eUyJoOrB1Crc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739274279; c=relaxed/simple; bh=1blVE50JLGY1NZ5lRXIT4Y/pMISIL1X/dC/cJ9LqPAo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UrLg4youpbJaDk2kMmvoaUD8AJU9r4RBW4sJNwQLWVa1HLrf31cQlLP/caT/PCfN2bPhiOKg5A/LCdNFhuMoMGj55PyfVjRVEU2GM6OsVN7PD9zRpFTwhbpLAss8aWLppAATgINjrA9wBUOse4/g48NGAvMzpylV2KlHFm2tB2M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=kOpRjjYM; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="kOpRjjYM" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1739274273; bh=1blVE50JLGY1NZ5lRXIT4Y/pMISIL1X/dC/cJ9LqPAo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kOpRjjYMhtXgmXvG9+mO0EsEoYtOddONqEUsg0WFZbS6kB5sl6TslmYIDiusbBKlz 1LfsUCMCBxHfF24s+nBN96Q6KVmCrrl4K4bwUQGP3nJJ0lQBK8YmLU+BrQOWZKk+YD //FIIbQHyC9ldmrdRn7IMVJewkLgQY20aPE1AfEUhCwPfrOPW48QwJvuLnBi+qOfrF WnDgJ/xCEesQD2tTe7NbzDax+HSURlOpYReSWALchDxoXp8vZ9ldPh/4+TCg3y8Uxy OCuSmnmQ7Ulak6lCfi44PirH4K879HSNWRAbojJNSFVoa6GfeG0IfESQwlgw9Uok0H iApzGPgvDWsWw== Received: from IcarusMOD.eternityproject.eu (2-237-20-237.ip236.fastwebnet.it [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: kholk11) by bali.collaboradmins.com (Postfix) with ESMTPSA id AC3AC17E1539; Tue, 11 Feb 2025 12:44:32 +0100 (CET) From: AngeloGioacchino Del Regno To: neil.armstrong@linaro.org Cc: quic_jesszhan@quicinc.com, airlied@gmail.com, simona@ffwll.ch, maarten.lankhorst@linux.intel.com, mripard@kernel.org, tzimmermann@suse.de, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, angelogioacchino.delregno@collabora.com, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, kernel@collabora.com, pablo.sun@mediatek.com Subject: [PATCH v1 1/2] dt-bindings: display: panel: Add Himax HX8279/HX8279-D Date: Tue, 11 Feb 2025 12:44:28 +0100 Message-ID: <20250211114429.1519148-2-angelogioacchino.delregno@collabora.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250211114429.1519148-1-angelogioacchino.delregno@collabora.com> References: <20250211114429.1519148-1-angelogioacchino.delregno@collabora.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Himax HX8279 is a Display DriverIC suitable for driving LCD MIPI-DSI panels. Describe this DriverIC and the Startek KD070FHFID078 panel found on newer revisions of the MediaTek Genio 510/700/1200 Evaluation Kits (EVK). Signed-off-by: AngeloGioacchino Del Regno Acked-by: Conor Dooley --- .../bindings/display/panel/himax,hx8279.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/himax,h= x8279.yaml diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx8279.y= aml b/Documentation/devicetree/bindings/display/panel/himax,hx8279.yaml new file mode 100644 index 000000000000..d66191398f50 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/himax,hx8279.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/himax,hx8279.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Himax HX8279/HX8279-D based MIPI-DSI panels + +maintainers: + - AngeloGioacchino Del Regno + +description: + The Himax HX8279 is a 1803 channel outputs source driver with MIPI + TCON, which generates the horizontal and vertical control timing to + the source and gate drivers. + This DriverIC is most suitable for 1200x1920, 1080x1920, 1200x1600, + and 600x1024 panels and outputs full RGB888 over two or four lanes, + single or dual, MIPI-DSI video interface. + +allOf: + - $ref: panel-common-dual.yaml# + +properties: + compatible: + items: + - enum: + - startek,kd070fhfid078 + - const: himax,hx8279 + + reg: + maxItems: 1 + + iovcc-supply: + description: I/O voltage supply + + vdd-supply: + description: Panel power supply + +required: + - compatible + - reg + - backlight + - reset-gpios + - iovcc-supply + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + panel@0 { + compatible =3D "startek,kd070fhfid078", "himax,hx8279"; + reg =3D <0>; + backlight =3D <&backlight>; + enable-gpios =3D <&pio 25 GPIO_ACTIVE_HIGH>; + reset-gpios =3D <&pio 45 GPIO_ACTIVE_HIGH>; + iovcc-supply =3D <&vreg_lcm_vio>; + vdd-supply =3D <&vreg_lcm_vdd>; + + port { + panel_in: endpoint { + remote-endpoint =3D <&dsi_out>; + }; + }; + }; + }; + +... --=20 2.48.1 From nobody Thu Dec 18 10:37:43 2025 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FAEB1F9F62; Tue, 11 Feb 2025 11:44:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739274279; cv=none; b=HRn+xi9nuNsYpSjmiPn2zvraOzJ3rrbbtZJ7E6h1S7DTBYTE58Ff9qn3oTbXvxv771qRwzTq4tlnTIfxIBdfS3A7D2FTP9VaRlU3KoPCeYLmJHoIz89UHuP49WnAlAosXjn/zc07WF+5H+g7fokRoOz2Rg9w0gRmBLHRYIf3xGw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739274279; c=relaxed/simple; bh=nqhPW+KlMIxzGEgQvxmG+jjwSFwvYVFHYY9fVsJwNjQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CuzNqj69gBbyiJVKVya+Oi7vmTknjQs/b+I7X4Xd9uBmAzmsl1IaQB+LWptneKodpD+EZhrgG/Qfub1Ruc32Tv6BERiDjGDqXlRsqx/9up0/bAqjb4XHCWLHKAurWZJIbIyaG4fX72kXzwoPrKnfaYKpcoWtx3wVF9SEoPJjqUw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=ogAO2GLU; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="ogAO2GLU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1739274274; bh=nqhPW+KlMIxzGEgQvxmG+jjwSFwvYVFHYY9fVsJwNjQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ogAO2GLUw7IA0egWvCktQeLajdA6N7IXdP2WB7NwcRUnH5usid9j+Cdafsz4atB99 El3xYi5S21wt0228dbs28ulaEyQpEPTdD9kbkJgIiuNtRlM1uICrsSpU5Tsr4jIsci tnaUOpKEt56xDufnYRhjQ/1aJWGFyks5TPyoYtgnoodN2ao/PgsVzklatN4B3hX8wl WDjpfONamSuDlWfTVg9hiTDZSapExMMeQPeWDYd9fCErvcV6dqCpSn2l2ph1pH8e1b XCSjZI/Ax10HXMzvfh3dVNmPAMbcuYXCGrhXqZcO6SKYwk5188bNJltJueXvQVJypr lIQ4waD93Wclw== Received: from IcarusMOD.eternityproject.eu (2-237-20-237.ip236.fastwebnet.it [2.237.20.237]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: kholk11) by bali.collaboradmins.com (Postfix) with ESMTPSA id D653F17E1542; Tue, 11 Feb 2025 12:44:33 +0100 (CET) From: AngeloGioacchino Del Regno To: neil.armstrong@linaro.org Cc: quic_jesszhan@quicinc.com, airlied@gmail.com, simona@ffwll.ch, maarten.lankhorst@linux.intel.com, mripard@kernel.org, tzimmermann@suse.de, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, angelogioacchino.delregno@collabora.com, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, kernel@collabora.com, pablo.sun@mediatek.com Subject: [PATCH v1 2/2] drm: panel: Add driver for Himax HX8279 and Startek KD070FHFID078 Date: Tue, 11 Feb 2025 12:44:29 +0100 Message-ID: <20250211114429.1519148-3-angelogioacchino.delregno@collabora.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250211114429.1519148-1-angelogioacchino.delregno@collabora.com> References: <20250211114429.1519148-1-angelogioacchino.delregno@collabora.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a driver for the Himax HX8279-D MIPI-DSI DriverIC with support for the Startek KX070FHFID078 7.0" 1200x1920 IPS panel, found on various MediaTek Genio Evaluation Kit boards. Signed-off-by: AngeloGioacchino Del Regno --- drivers/gpu/drm/panel/Kconfig | 11 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-himax-hx8279.c | 910 +++++++++++++++++++++ 3 files changed, 922 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-himax-hx8279.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index d7469c565d1d..678c8aabf311 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -154,6 +154,17 @@ config DRM_PANEL_LVDS handling of power supplies or control signals. It implements automatic backlight handling if the panel is attached to a backlight controller. =20 +config DRM_PANEL_HIMAX_HX8279 + tristate "Himax HX8279-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + Himax HX8279 controller, such as the Startek KD070FHFID078 + 7.0" 1200x1920 IPS LCD panel that uses a MIPI-DSI interface + and others. + config DRM_PANEL_HIMAX_HX83102 tristate "Himax HX83102-based panels" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 7dcf72646cac..d55698d7f287 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) +=3D panel-ebbg-ft871= 9.o obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) +=3D panel-elida-kd35t133.o obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) +=3D panel-feixin-k101-im2ba02= .o obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) +=3D panel-feiyang-fy07024= di26a30d.o +obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) +=3D panel-himax-hx8279.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) +=3D panel-himax-hx83102.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) +=3D panel-himax-hx83112a.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) +=3D panel-himax-hx8394.o diff --git a/drivers/gpu/drm/panel/panel-himax-hx8279.c b/drivers/gpu/drm/p= anel/panel-himax-hx8279.c new file mode 100644 index 000000000000..b1a22059fd0f --- /dev/null +++ b/drivers/gpu/drm/panel/panel-himax-hx8279.c @@ -0,0 +1,910 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Himax HX8279 DriverIC panels driver + * + * Copyright (c) 2025 Collabora Ltd. + * AngeloGioacchino Del Regno + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Page selection */ +#define HX8279_REG_PAGE 0xb0 + #define HX8279_PAGE_SEL GENMASK(3, 0) + +/* Page 0 - Driver/Module Configuration */ +#define HX8279_P0_VGHS 0xbf +#define HX8279_P0_VGLS 0xc0 +#define HX8279_P0_VGPHS 0xc2 +#define HX8279_P0_VGNHS 0xc4 + #define HX8279_P0_VG_SEL GENMASK(4, 0) + #define HX8279_VGH_MIN_MV 8700 + #define HX8279_VGH_STEP_MV 300 + #define HX8279_VGL_MIN_MV 6700 + #define HX8279_VGL_STEP_MV 300 + #define HX8279_VGPNH_MIN_MV 4000 + #define HX8279_VGPNX_STEP_MV 50 + #define HX8279_VGH_VOLT_SEL(x) ((x - HX8279_VGH_MIN_MV) / HX8279_VGH_STE= P_MV) + #define HX8279_VGL_VOLT_SEL(x) ((x - HX8279_VGL_MIN_MV) / HX8279_VGL_STE= P_MV) + #define HX8279_VGPN_VOLT_SEL(x) ((x - HX8279_VGPNH_MIN_MV) / HX8279_VGPNX= _STEP_MV) + +/* Page 1 - Gate driver On Array (GOA) Timing */ +#define HX8279_P1_REG_GOA_L 0xc0 +#define HX8279_P1_REG_GOUTL(x) (HX8279_P1_REG_GOA_L + (x)) +#define HX8279_P1_REG_GOA_R 0xd4 +#define HX8279_P1_REG_GOUTR(x) (HX8279_P1_REG_GOA_R + (x)) + #define HX8279_GOUT_STB GENMASK(7, 6) + #define HX8279_GOUT_SEL GENMASK(5, 0) + +/* Page 3 - Gate driver On Array (GOA) Configuration */ +#define HX8279_P3_REG_UNKNOWN_BA 0xba +#define HX8279_P3_REG_GOA_STVL 0xc8 + #define HX8279_P3_GOA_STV_LEAD GENMASK(4, 0) +#define HX8279_P3_REG_GOA_CKVL 0xc9 + #define HX8279_P3_GOA_CKV_LEAD GENMASK(4, 0) +#define HX8279_P3_REG_GOA_CKVD 0xca + #define HX8279_P3_GOA_CKV_NONOVERLAP BIT(7) + #define HX8279_P3_GOA_CKV_RESERVED BIT(6) + #define HX8279_P3_GOA_CKV_DUMMY GENMASK(5, 0) +#define HX8279_P3_REG_GOA_CLR1_W_ADJ 0xd2 +#define HX8279_P3_REG_GOA_CLR234_W_ADJ 0xd3 +#define HX8279_P3_REG_GOA_CLR1_CFG 0xd4 +#define HX8279_P3_REG_GOA_CLR_CFG(x) (HX8279_P3_REG_GOA_CLR1_CFG + x) /* C= LR1..4 */ + #define HX8279_P3_GOA_CLR_CFG_POLARITY BIT(7) + #define HX8279_P3_GOA_CLR_CFG_STARTPOS GENMASK(6, 0) +#define HX8279_P3_REG_UNKNOWN_E4 0xe4 +#define HX8279_P3_REG_UNKNOWN_E5 0xe5 + +/* Page 5 - MIPI */ +#define HX8279_P5_REG_TIMING 0xb3 + #define HX8279_P5_TIMING_THS_SETTLE GENMASK(7, 5) + #define HX8279_P5_TIMING_LHS_SETTLE BIT(4) + #define HX8279_P5_TIMING_TLPX GENMASK(3, 0) +#define HX8279_P5_REG_UNKNOWN_B8 0xb8 +#define HX8279_P5_REG_UNKNOWN_BC 0xbc +#define HX8279_P5_REG_UNKNOWN_D6 0xd6 + +/* Page 6 - Engineer */ +#define HX8279_P6_REG_ENGINEER_PWD 0xb8 +#define HX8279_P6_REG_INHOUSE_FUNC 0xc0 + #define HX8279_P6_ENG_UNLOCK_WORD 0xa5 +#define HX8279_P6_REG_GAMMA_CHOPPER 0xbc + #define HX8279_P6_GAMMA_POCGM_CTL GENMASK(6, 4) + #define HX8279_P6_GAMMA_POGCMD_CTL GENMASK(2, 0) +#define HX8279_P6_REG_DLY_TIME_ADJ 0xd5 + +struct hx8279 { + struct drm_panel panel; + struct mipi_dsi_device *dsi[2]; + struct regulator_bulk_data vregs[2]; + struct gpio_desc *enable_gpio; + struct gpio_desc *reset_gpio; + const struct hx8279_panel_desc *desc; + u8 last_page; + bool skip_voltage_config; + bool skip_goa_timing; + bool skip_goa_config; + bool skip_mipi_timing; + bool skip_gamma_config; +}; + +struct hx8279_panel_mode { + const struct drm_display_mode mode; + int bpc; + bool is_video_mode; +}; + +struct hx8279_panel_desc { + const char *model_name; + const struct mipi_dsi_device_info dsi_info; + const struct hx8279_panel_mode *mode_data; + bool is_dual_dsi; + u8 num_lanes; + u8 num_modes; + + /* Page 0 */ + unsigned int vgh_mv; + unsigned int vgl_mv; + unsigned int vgph_mv; + unsigned int vgnh_mv; + + /* Page 1 */ + u8 gout_l[20]; + u8 gout_r[20]; + + /* Page 3 */ + u8 goa_unk_ba; + u8 goa_stv_lead_time_ck; + u8 goa_ckv_lead_time_ck; + u8 goa_ckv_dummy_vblank_num; + bool goa_ckv_non_overlap_ctl; + u8 goa_clr1_width_adj; + u8 goa_clr234_width_adj; + s8 goa_clr_polarity[4]; + int goa_clr_start_pos[4]; + u8 goa_unk_e4; + u8 goa_unk_e5; + + /* Page 5 */ + u8 bta_tlpx; + bool lhs_settle_time_by_osc25; + u8 ths_settle_time; + u8 timing_unk_b8; + u8 timing_unk_bc; + u8 timing_unk_d6; + + /* Page 6 */ + u8 gamma_ctl; + u8 src_delay_time_adj_ck; +}; + +static inline struct hx8279 *to_hx8279(struct drm_panel *panel) +{ + return container_of(panel, struct hx8279, panel); +} + +static int hx8279_set_page(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx, u8 page) +{ + const u8 cmd_set_page[] =3D { HX8279_REG_PAGE, page }; + + if (hx->last_page =3D=3D page) + return 0; + + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_page, ARRAY_SIZE(cmd_set_pa= ge)); + if (!dsi_ctx->accum_err) + hx->last_page =3D page; + else + return -EINVAL; + + return 0; +} + +static int hx8279_set_module_config(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + u8 cmd_set_voltage[2]; + int ret; + + if (hx->skip_voltage_config) + return 0; + + /* Page 0 - Driver/Module Configuration */ + ret =3D hx8279_set_page(hx, dsi_ctx, 0); + if (ret) + return ret; + + if (desc->vgh_mv) { + cmd_set_voltage[0] =3D HX8279_P0_VGHS; + cmd_set_voltage[1] =3D HX8279_VGH_VOLT_SEL(desc->vgh_mv); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, + ARRAY_SIZE(cmd_set_voltage)); + } + + if (desc->vgl_mv) { + cmd_set_voltage[0] =3D HX8279_P0_VGLS; + cmd_set_voltage[1] =3D HX8279_VGL_VOLT_SEL(desc->vgl_mv); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, + ARRAY_SIZE(cmd_set_voltage)); + } + + if (desc->vgph_mv) { + cmd_set_voltage[0] =3D HX8279_P0_VGPHS; + cmd_set_voltage[1] =3D HX8279_VGPN_VOLT_SEL(desc->vgph_mv); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, + ARRAY_SIZE(cmd_set_voltage)); + } + + if (desc->vgnh_mv) { + cmd_set_voltage[0] =3D HX8279_P0_VGNHS; + cmd_set_voltage[1] =3D HX8279_VGPN_VOLT_SEL(desc->vgnh_mv); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, + ARRAY_SIZE(cmd_set_voltage)); + } + + return 0; +} + +static int hx8279_set_gout(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + u8 cmd_set_gout[2]; + int i, ret; + + if (hx->skip_goa_timing) + return 0; + + ret =3D hx8279_set_page(hx, dsi_ctx, 1); + if (ret) + return ret; + + for (i =3D 0; i < ARRAY_SIZE(desc->gout_l); i++) { + cmd_set_gout[0] =3D HX8279_P1_REG_GOUTL(i); + cmd_set_gout[1] =3D desc->gout_l[i]; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gout, + ARRAY_SIZE(cmd_set_gout)); + } + + for (i =3D 0; i < ARRAY_SIZE(desc->gout_r); i++) { + cmd_set_gout[0] =3D HX8279_P1_REG_GOUTR(i); + cmd_set_gout[1] =3D desc->gout_r[i]; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gout, + ARRAY_SIZE(cmd_set_gout)); + } + + return 0; +} + +static int hx8279_set_goa_cfg(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + u8 cmd_set_goa[2]; + int i, ret; + + if (hx->skip_goa_config) + return 0; + + ret =3D hx8279_set_page(hx, dsi_ctx, 3); + if (ret) + return ret; + + if (desc->goa_unk_ba) { + cmd_set_goa[0] =3D HX8279_P3_REG_UNKNOWN_BA; + cmd_set_goa[1] =3D desc->goa_unk_ba; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_stv_lead_time_ck) { + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_STVL; + cmd_set_goa[1] =3D FIELD_PREP(HX8279_P3_GOA_STV_LEAD, + desc->goa_stv_lead_time_ck); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_ckv_lead_time_ck) { + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_CKVL; + cmd_set_goa[1] =3D FIELD_PREP(HX8279_P3_GOA_CKV_DUMMY, + desc->goa_ckv_lead_time_ck); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_ckv_dummy_vblank_num) { + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_CKVD; + cmd_set_goa[1] =3D FIELD_PREP(HX8279_P3_GOA_CKV_LEAD, + desc->goa_ckv_dummy_vblank_num); + cmd_set_goa[1] |=3D FIELD_PREP(HX8279_P3_GOA_CKV_NONOVERLAP, + desc->goa_ckv_non_overlap_ctl); + /* RESERVED must be always set */ + cmd_set_goa[1] |=3D HX8279_P3_GOA_CKV_RESERVED; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_clr1_width_adj) { + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_CLR1_W_ADJ; + cmd_set_goa[1] =3D desc->goa_clr1_width_adj; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_clr234_width_adj) { + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_CLR234_W_ADJ; + cmd_set_goa[1] =3D desc->goa_clr234_width_adj; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + /* Polarity and Start Position arrays are of the same size */ + for (i =3D 0; i < ARRAY_SIZE(desc->goa_clr_polarity); i++) { + if (desc->goa_clr_polarity[i] < 0 || desc->goa_clr_start_pos[i] < 0) + continue; + + cmd_set_goa[0] =3D HX8279_P3_REG_GOA_CLR_CFG(i); + cmd_set_goa[1] =3D FIELD_PREP(HX8279_P3_GOA_CLR_CFG_STARTPOS, + desc->goa_clr_start_pos[i]); + cmd_set_goa[1] |=3D FIELD_PREP(HX8279_P3_GOA_CLR_CFG_POLARITY, + desc->goa_clr_polarity[i]); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + if (desc->goa_unk_e4) { + cmd_set_goa[0] =3D HX8279_P3_REG_UNKNOWN_E4; + cmd_set_goa[1] =3D desc->goa_unk_e4; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + } + + cmd_set_goa[0] =3D HX8279_P3_REG_UNKNOWN_E5; + cmd_set_goa[1] =3D desc->goa_unk_e5; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, + ARRAY_SIZE(cmd_set_goa)); + + return 0; +} + +static int hx8279_set_mipi_cfg(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + u8 cmd_set_mipi[2]; + int ret; + + if (hx->skip_mipi_timing) + return 0; + + ret =3D hx8279_set_page(hx, dsi_ctx, 5); + if (ret) + return ret; + + if (desc->bta_tlpx || desc->ths_settle_time || desc->lhs_settle_time_by_o= sc25) { + cmd_set_mipi[0] =3D HX8279_P5_REG_TIMING; + cmd_set_mipi[1] =3D FIELD_PREP(HX8279_P5_TIMING_TLPX, desc->bta_tlpx); + cmd_set_mipi[1] |=3D FIELD_PREP(HX8279_P5_TIMING_THS_SETTLE, + desc->ths_settle_time); + cmd_set_mipi[1] |=3D FIELD_PREP(HX8279_P5_TIMING_LHS_SETTLE, + desc->lhs_settle_time_by_osc25); + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, + ARRAY_SIZE(cmd_set_mipi)); + } + + if (desc->timing_unk_b8) { + cmd_set_mipi[0] =3D HX8279_P5_REG_UNKNOWN_B8; + cmd_set_mipi[1] =3D desc->timing_unk_b8; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, + ARRAY_SIZE(cmd_set_mipi)); + } + + if (desc->timing_unk_bc) { + cmd_set_mipi[0] =3D HX8279_P5_REG_UNKNOWN_BC; + cmd_set_mipi[1] =3D desc->timing_unk_bc; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, + ARRAY_SIZE(cmd_set_mipi)); + } + + if (desc->timing_unk_d6) { + cmd_set_mipi[0] =3D HX8279_P5_REG_UNKNOWN_D6; + cmd_set_mipi[1] =3D desc->timing_unk_d6; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, + ARRAY_SIZE(cmd_set_mipi)); + } + + return 0; +} + +static int hx8279_set_adv_cfg(struct hx8279 *hx, + struct mipi_dsi_multi_context *dsi_ctx) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + const u8 cmd_set_gamma[] =3D { HX8279_P6_REG_GAMMA_CHOPPER, desc->gamma_c= tl }; + const u8 cmd_set_dly[] =3D { HX8279_P6_REG_DLY_TIME_ADJ, desc->src_delay_= time_adj_ck }; + u8 cmd_set_eng[] =3D { HX8279_P6_REG_ENGINEER_PWD, HX8279_P6_ENG_UNLOCK_W= ORD }; + int ret; + + if (!desc->gamma_ctl && !desc->src_delay_time_adj_ck) + return 0; + + ret =3D hx8279_set_page(hx, dsi_ctx, 6); + if (ret) + return ret; + + /* Unlock ENG settings: write same word to both ENGINEER_PWD and INHOUSE_= FUNC */ + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng= )); + + cmd_set_eng[0] =3D HX8279_P6_REG_INHOUSE_FUNC; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng= )); + + /* Set Gamma Chopper and Gamma buffer Chopper control */ + if (desc->gamma_ctl) + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gamma, + ARRAY_SIZE(cmd_set_gamma)); + + /* Set Source delay time adjustment (CKV falling to Source off) */ + if (desc->src_delay_time_adj_ck) + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_dly, + ARRAY_SIZE(cmd_set_dly)); + + /* Lock ENG settings again */ + cmd_set_eng[0] =3D HX8279_P6_REG_ENGINEER_PWD; + cmd_set_eng[1] =3D 0; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng= )); + + cmd_set_eng[0] =3D HX8279_P6_REG_INHOUSE_FUNC; + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng= )); + + return 0; +} + +static int hx8279_on(struct hx8279 *hx) +{ + struct mipi_dsi_device *dsi =3D hx->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D dsi }; + int ret; + + /* Page 5 */ + ret =3D hx8279_set_mipi_cfg(hx, &dsi_ctx); + if (ret) + return ret; + + /* Page 1 */ + ret =3D hx8279_set_gout(hx, &dsi_ctx); + if (ret) + return ret; + + /* Page 3 */ + ret =3D hx8279_set_goa_cfg(hx, &dsi_ctx); + if (ret) + return ret; + + /* Page 0 - Driver/Module Configuration */ + ret =3D hx8279_set_module_config(hx, &dsi_ctx); + if (ret) + return ret; + + /* Page 6 */ + ret =3D hx8279_set_adv_cfg(hx, &dsi_ctx); + if (ret) + return ret; + + if (dsi_ctx.accum_err) + return dsi_ctx.accum_err; + + return 0; +} + +static void hx8279_power_off(struct hx8279 *hx) +{ + gpiod_set_value_cansleep(hx->reset_gpio, 0); + usleep_range(100, 500); + gpiod_set_value_cansleep(hx->enable_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(hx->vregs), hx->vregs); +} + +static int hx8279_disable(struct drm_panel *panel) +{ + struct hx8279 *hx =3D to_hx8279(panel); + struct mipi_dsi_device *dsi =3D hx->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D dsi }; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + + return 0; +} + +static int hx8279_enable(struct drm_panel *panel) +{ + struct hx8279 *hx =3D to_hx8279(panel); + struct mipi_dsi_device *dsi =3D hx->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D dsi }; + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + return 0; +} + +static int hx8279_prepare(struct drm_panel *panel) +{ + struct hx8279 *hx =3D to_hx8279(panel); + struct mipi_dsi_device *dsi =3D hx->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D dsi }; + int ret; + + ret =3D regulator_bulk_enable(ARRAY_SIZE(hx->vregs), hx->vregs); + if (ret) + return ret; + + gpiod_set_value_cansleep(hx->enable_gpio, 1); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(hx->reset_gpio, 1); + usleep_range(6000, 7000); + + ret =3D hx8279_on(hx); + if (ret) { + hx8279_power_off(hx); + return ret; + } + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + + dsi->mode_flags |=3D MIPI_DSI_MODE_LPM; + if (hx->dsi[1]) + hx->dsi[1]->mode_flags |=3D MIPI_DSI_MODE_LPM; + + return 0; +} + +static int hx8279_unprepare(struct drm_panel *panel) +{ + struct hx8279 *hx =3D to_hx8279(panel); + struct mipi_dsi_device *dsi =3D hx->dsi[0]; + struct mipi_dsi_multi_context dsi_ctx =3D { .dsi =3D dsi }; + + dsi->mode_flags &=3D ~MIPI_DSI_MODE_LPM; + if (hx->dsi[1]) + hx->dsi[1]->mode_flags &=3D ~MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + + hx8279_power_off(hx); + return 0; +} + +static int hx8279_get_modes(struct drm_panel *panel, struct drm_connector = *connector) +{ + struct hx8279 *hx =3D to_hx8279(panel); + int i; + + for (i =3D 0; i < hx->desc->num_modes; i++) { + struct drm_display_mode *mode; + + mode =3D drm_mode_duplicate(connector->dev, &hx->desc->mode_data[i].mode= ); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type |=3D DRM_MODE_TYPE_DRIVER; + if (hx->desc->num_modes =3D=3D 1) + mode->type |=3D DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc =3D hx->desc->mode_data[0].bpc; + connector->display_info.height_mm =3D hx->desc->mode_data[0].mode.height_= mm; + connector->display_info.width_mm =3D hx->desc->mode_data[0].mode.width_mm; + + return hx->desc->num_modes; +} + +static const struct drm_panel_funcs hx8279_panel_funcs =3D { + .disable =3D hx8279_disable, + .unprepare =3D hx8279_unprepare, + .prepare =3D hx8279_prepare, + .enable =3D hx8279_enable, + .get_modes =3D hx8279_get_modes, +}; + +static int hx8279_init_vregs(struct hx8279 *hx, struct device *dev) +{ + int ret; + + hx->vregs[0].supply =3D "vdd"; + hx->vregs[1].supply =3D "iovcc"; + ret =3D devm_regulator_bulk_get(dev, ARRAY_SIZE(hx->vregs), + hx->vregs); + if (ret < 0) + return ret; + + ret =3D regulator_is_supported_voltage(hx->vregs[0].consumer, + 3000000, 5000000); + if (!ret) + return -EINVAL; + + ret =3D regulator_is_supported_voltage(hx->vregs[1].consumer, + 1700000, 1900000); + if (!ret) + return -EINVAL; + + return 0; +} + +static int hx8279_check_config(struct hx8279 *hx, struct device *dev) +{ + const struct hx8279_panel_desc *desc =3D hx->desc; + int i, num_clr, num_zero; + + /* Voltages config validation */ + if (!desc->vgh_mv && !desc->vgl_mv && !desc->vgph_mv && !desc->vgnh_mv) + hx->skip_voltage_config =3D true; + else if ((desc->vgh_mv && desc->vgh_mv < HX8279_VGH_MIN_MV) || + (desc->vgl_mv && desc->vgl_mv < HX8279_VGL_MIN_MV) || + (desc->vgph_mv && desc->vgph_mv < HX8279_VGPNH_MIN_MV) || + (desc->vgnh_mv && desc->vgnh_mv < HX8279_VGPNH_MIN_MV)) + return -EINVAL; + + /* GOA Timing validation */ + num_zero =3D 1; + for (i =3D 0; i < ARRAY_SIZE(desc->gout_l); i++) { + if (!desc->gout_l[i]) + num_zero++; + else if (desc->gout_l[i] > (HX8279_GOUT_STB | HX8279_GOUT_SEL)) + return dev_err_probe(dev, -EINVAL, + "Invalid value found in gout_l[%d]\n", i); + } + if (num_zero >=3D ARRAY_SIZE(desc->gout_l)) + hx->skip_goa_timing =3D true; + + if (!hx->skip_goa_timing) { + num_zero =3D 1; + for (i =3D 0; i < ARRAY_SIZE(desc->gout_r); i++) { + if (!desc->gout_r[i]) + num_zero++; + else if (desc->gout_r[i] > (HX8279_GOUT_STB | HX8279_GOUT_SEL)) + return dev_err_probe(dev, -EINVAL, + "Invalid value found in gout_r[%d]\n", i); + } + if (num_zero >=3D ARRAY_SIZE(desc->gout_r)) + hx->skip_goa_timing =3D true; + } + + /* GOA Configuration validation */ + if (!desc->goa_unk_ba && !desc->goa_stv_lead_time_ck && + !desc->goa_ckv_lead_time_ck && !desc->goa_ckv_dummy_vblank_num && + !desc->goa_clr1_width_adj && !desc->goa_clr234_width_adj && + !desc->goa_unk_e4 && !desc->goa_unk_e5) + hx->skip_goa_config =3D true; + + if (!hx->skip_goa_config) { + if ((desc->goa_stv_lead_time_ck > HX8279_P3_GOA_STV_LEAD) || + (desc->goa_ckv_lead_time_ck > HX8279_P3_GOA_CKV_LEAD) || + (desc->goa_ckv_dummy_vblank_num > HX8279_P3_GOA_CKV_DUMMY)) + return dev_err_probe(dev, -EINVAL, + "Invalid lead timings in GOA config\n"); + /* + * Don't perform zero check for polarity and start position, as + * both pol=3D0 and start=3D0 are valid configuration values. + */ + num_clr =3D 0; + for (i =3D 0; i < ARRAY_SIZE(desc->goa_clr_start_pos); i++) { + if (desc->goa_clr_start_pos[i] < 0) + continue; + else if (desc->goa_clr_start_pos[i] > HX8279_P3_GOA_CLR_CFG_STARTPOS) + return dev_err_probe(dev, -EINVAL, + "Invalid start position for CLR%d\n", i + 1); + else + num_clr++; + } + if (!num_clr) + return -EINVAL; + + for (i =3D 0; i < ARRAY_SIZE(desc->goa_clr_polarity); i++) { + if (num_clr < 0) + return -EINVAL; + + if (desc->goa_clr_polarity[i] < 0) + continue; + else if (desc->goa_clr_polarity[i] > 1) + return dev_err_probe(dev, -EINVAL, + "Invalid polarity for CLR%d\n", i + 1); + else if (desc->goa_clr_polarity[i] >=3D 0) + num_clr--; + } + } + + /* MIPI Configuration validation */ + if (!desc->bta_tlpx && !desc->lhs_settle_time_by_osc25 && + !desc->ths_settle_time && !desc->timing_unk_b8 && + !desc->timing_unk_bc && !desc->timing_unk_d6) + hx->skip_mipi_timing =3D true; + + /* Gamma Configuration validation */ + if (desc->gamma_ctl > (HX8279_P6_GAMMA_POCGM_CTL | HX8279_P6_GAMMA_POGCMD= _CTL)) + return -EINVAL; + + return 0; +} + +static int hx8279_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev =3D &dsi->dev; + struct hx8279 *hx; + int i, num_dsis =3D 1, ret; + + hx =3D devm_kzalloc(dev, sizeof(*hx), GFP_KERNEL); + if (!hx) + return -ENOMEM; + + ret =3D hx8279_init_vregs(hx, dev); + if (ret) + return ret; + + hx->desc =3D device_get_match_data(dev); + if (!hx->desc) + return -ENODEV; + + /* + * In some DriverICs some or all fields may be OTP: perform a + * basic configuration check before writing to help avoiding + * irreparable mistakes. + * + * Please note that this is not perfect and will only check if + * the values may be plausible; values that are wrong for a + * specific display, but still plausible for DrIC config will + * be accepted. + */ + ret =3D hx8279_check_config(hx, dev); + if (ret) + return dev_err_probe(dev, ret, "Invalid DriverIC configuration\n"); + + /* The enable line may be always tied to VCCIO, so this is optional */ + hx->enable_gpio =3D devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS); + if (IS_ERR(hx->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(hx->enable_gpio), + "Failed to get enable GPIO\n"); + + hx->reset_gpio =3D devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(hx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(hx->reset_gpio), + "Failed to get reset GPIO\n"); + + /* If the panel is connected on two DSIs then DSI0 left, DSI1 right */ + if (hx->desc->is_dual_dsi) { + const struct mipi_dsi_device_info *info =3D &hx->desc->dsi_info; + struct mipi_dsi_host *dsi_r_host; + struct device_node *dsi_r; + + dsi_r =3D of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi_r) + return dev_err_probe(dev, -ENODEV, "Cannot get secondary DSI node.\n"); + + dsi_r_host =3D of_find_mipi_dsi_host_by_node(dsi_r); + of_node_put(dsi_r); + if (!dsi_r_host) + return dev_err_probe(dev, -EPROBE_DEFER, + "Cannot get secondary DSI host\n"); + + hx->dsi[1] =3D devm_mipi_dsi_device_register_full(dev, dsi_r_host, info); + if (IS_ERR(hx->dsi[1])) + return dev_err_probe(dev, PTR_ERR(hx->dsi[1]), + "Cannot get secondary DSI node\n"); + num_dsis++; + mipi_dsi_set_drvdata(hx->dsi[1], hx); + } + + hx->dsi[0] =3D dsi; + mipi_dsi_set_drvdata(dsi, hx); + + drm_panel_init(&hx->panel, dev, &hx8279_panel_funcs, DRM_MODE_CONNECTOR_D= SI); + + ret =3D drm_panel_of_backlight(&hx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&hx->panel); + + for (i =3D 0; i < num_dsis; i++) { + hx->dsi[i]->lanes =3D hx->desc->num_lanes; + hx->dsi[i]->format =3D MIPI_DSI_FMT_RGB888; + + hx->dsi[i]->mode_flags =3D MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM; + + if (hx->desc->mode_data[0].is_video_mode) + hx->dsi[i]->mode_flags |=3D MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + + ret =3D devm_mipi_dsi_attach(dev, hx->dsi[i]); + if (ret < 0) { + /* If we fail to attach to either host, we're done */ + if (num_dsis =3D=3D 2) + mipi_dsi_device_unregister(hx->dsi[1]); + + return dev_err_probe(dev, ret, + "Cannot attach to DSI%d host.\n", i); + } + } + + /* Make sure we start in a known state: panel off */ + gpiod_set_value_cansleep(hx->reset_gpio, 0); + gpiod_set_value_cansleep(hx->enable_gpio, 0); + + return 0; +} + +static void hx8279_remove(struct mipi_dsi_device *dsi) +{ + struct hx8279 *hx =3D mipi_dsi_get_drvdata(dsi); + + drm_panel_remove(&hx->panel); +} + +static const struct hx8279_panel_mode startek_kd070fhfid078_modes[] =3D { + { + .mode =3D { + .clock =3D 156458, + .hdisplay =3D 1200, + .hsync_start =3D 1200 + 50, + .hsync_end =3D 1200 + 50 + 24, + .htotal =3D 1200 + 50 + 24 + 66, + .vdisplay =3D 1920, + .vsync_start =3D 1920 + 14, + .vsync_end =3D 1920 + 14 + 2, + .vtotal =3D 1920 + 14 + 2 + 10, + .width_mm =3D 95, + .height_mm =3D 151, + .type =3D DRM_MODE_TYPE_DRIVER + }, + .bpc =3D 8, + .is_video_mode =3D true, + }, +}; + +static const struct hx8279_panel_desc startek_kd070fhfid078 =3D { + .model_name =3D "Startek KD070FHFID078", + .dsi_info =3D { + .type =3D "KD070FHFID078", + .channel =3D 0, + .node =3D NULL, + }, + .mode_data =3D startek_kd070fhfid078_modes, + .num_modes =3D ARRAY_SIZE(startek_kd070fhfid078_modes), + .num_lanes =3D 4, + + /* Driver/Module Configuration: LC Matrix voltages */ + .vgh_mv =3D 18000, + .vgl_mv =3D 12100, + .vgph_mv =3D 5500, + .vgnh_mv =3D 5500, + + /* Gate driver On Array (GOA) Timing */ + .gout_l =3D { 0xd, 0xd, 0x6, 0x6, 0x8, 0x8, 0xa, 0xa, 0xc, 0xc, + 0x0, 0x0, 0xe, 0xe, 0x1, 0x1, 0x4, 0x4, 0x0, 0x0 }, + .gout_r =3D { 0xd, 0xd, 0x5, 0x5, 0x7, 0x7, 0x9, 0x9, 0xb, 0xb, + 0x0, 0x0, 0xe, 0xe, 0x1, 0x1, 0x3, 0x3, 0x0, 0x0 }, + + /* Gate driver On Array (GOA) Configuration */ + .goa_unk_ba =3D 0xf0, + .goa_stv_lead_time_ck =3D 7, + .goa_ckv_lead_time_ck =3D 3, + .goa_ckv_dummy_vblank_num =3D 1, + .goa_clr1_width_adj =3D 1, + .goa_clr234_width_adj =3D 5, + .goa_clr_polarity =3D { 0, 1, -1, -1 }, + .goa_clr_start_pos =3D { 5, 10, -1, -1 }, + .goa_unk_e4 =3D 0xc0, + .goa_unk_e5 =3D 0x00, + + /* MIPI Configuration */ + .bta_tlpx =3D 2, + .lhs_settle_time_by_osc25 =3D true, + .ths_settle_time =3D 2, + .timing_unk_b8 =3D 0x7f, + .timing_unk_bc =3D 0x20, + .timing_unk_d6 =3D 0x7f, + + /* ENG/Gamma Configuration */ + .gamma_ctl =3D FIELD_PREP_CONST(HX8279_P6_GAMMA_POCGM_CTL, 1) | + FIELD_PREP_CONST(HX8279_P6_GAMMA_POGCMD_CTL, 1), + .src_delay_time_adj_ck =3D 72, +}; + +static const struct of_device_id hx8279_of_match[] =3D { + { .compatible =3D "startek,kd070fhfid078", .data =3D &startek_kd070fhfid0= 78 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, hx8279_of_match); + +static struct mipi_dsi_driver hx8279_driver =3D { + .probe =3D hx8279_probe, + .remove =3D hx8279_remove, + .driver =3D { + .name =3D "panel-himax-hx8279", + .of_match_table =3D hx8279_of_match, + }, +}; +module_mipi_dsi_driver(hx8279_driver); + +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_DESCRIPTION("Himax HX8279 DriverIC panels driver"); +MODULE_LICENSE("GPL"); --=20 2.48.1