Introduce the bindings and panel driver for the DJN LCD panel using
HX83112A driver IC.
Then we can add the panel to the device dts and also enable the GPU.
Signed-off-by: Luca Weiss <[email protected]>
---
Luca Weiss (3):
dt-bindings: display: panel: Add Himax HX83112A
drm/panel: Add driver for DJN HX83112A LCD panel
arm64: dts: qcom: sm7225-fairphone-fp4: Enable display and GPU
.../bindings/display/panel/himax,hx83112a.yaml | 75 +++++
arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts | 62 +++-
drivers/gpu/drm/panel/Kconfig | 9 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-himax-hx83112a.c | 372 +++++++++++++++++++++
5 files changed, 510 insertions(+), 9 deletions(-)
---
base-commit: e9ad8e6186dbc420e26d2ffbb05cdce33fbf041d
change-id: 20240105-fp4-panel-50aba8536a0a
Best regards,
--
Luca Weiss <[email protected]>
Himax HX83112A is a display driver IC used to drive LCD DSI panels.
Describe it.
Signed-off-by: Luca Weiss <[email protected]>
---
.../bindings/display/panel/himax,hx83112a.yaml | 75 ++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx83112a.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx83112a.yaml
new file mode 100644
index 000000000000..22f58f1da9fd
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/himax,hx83112a.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/himax,hx83112a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Himax HX83112A-based DSI display panels
+
+maintainers:
+ - Luca Weiss <[email protected]>
+
+description:
+ The Himax HX83112A is a generic DSI Panel IC used to control
+ LCD panels.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: fairphone,fp4-hx83112a-djn
+ - const: himax,hx83112a
+
+ vdd1-supply:
+ description: Digital voltage rail
+
+ vsn-supply:
+ description: Positive source voltage rail
+
+ vsp-supply:
+ description: Negative source voltage rail
+
+ reg: true
+ port: true
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+ - vdd1-supply
+ - vsn-supply
+ - vsp-supply
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "fairphone,fp4-hx83112a-djn", "himax,hx83112a";
+ reg = <0>;
+
+ backlight = <&pm6150l_wled>;
+ reset-gpios = <&pm6150l_gpios 9 GPIO_ACTIVE_LOW>;
+
+ vdd1-supply = <&vreg_l1e>;
+ vsn-supply = <&pm6150l_lcdb_ncp>;
+ vsp-supply = <&pm6150l_lcdb_ldo>;
+
+ port {
+ panel_in_0: endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
--
2.43.0
Add the description for the display panel found on this phone and remove
the simple-framebuffer that was in place until now. Unfortunately the
LCDB module on PM6150L isn't yet supported upstream so we need to use a
dummy regulator-fixed in the meantime.
And with this done we can also enable the GPU.
Signed-off-by: Luca Weiss <[email protected]>
---
arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts | 62 +++++++++++++++++++----
1 file changed, 53 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
index ade619805519..8eb376f2db46 100644
--- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
+++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
@@ -45,15 +45,6 @@ chosen {
ranges;
stdout-path = "serial0:115200n8";
-
- framebuffer0: framebuffer@a000000 {
- compatible = "simple-framebuffer";
- reg = <0 0xa0000000 0 (2340 * 1080 * 4)>;
- width = <1080>;
- height = <2340>;
- stride = <(1080 * 4)>;
- format = "a8r8g8b8";
- };
};
gpio-keys {
@@ -68,6 +59,14 @@ key-volume-up {
};
};
+ /* Dummy regulator until PM6150L has LCDB VSP/VSN support */
+ lcdb_dummy: regulator-lcdb-dummy {
+ compatible = "regulator-fixed";
+ regulator-name = "lcdb_dummy";
+ regulator-min-microvolt = <5500000>;
+ regulator-max-microvolt = <5500000>;
+ };
+
reserved-memory {
/*
* The rmtfs memory region in downstream is 'dynamically allocated'
@@ -365,6 +364,10 @@ &cdsp {
status = "okay";
};
+&gmu {
+ status = "okay";
+};
+
&gpi_dma0 {
status = "okay";
};
@@ -373,6 +376,10 @@ &gpi_dma1 {
status = "okay";
};
+&gpu {
+ status = "okay";
+};
+
&i2c0 {
clock-frequency = <400000>;
status = "okay";
@@ -404,6 +411,43 @@ &ipa {
status = "okay";
};
+&mdss {
+ status = "okay";
+};
+
+&mdss_dsi0 {
+ vdda-supply = <&vreg_l22a>;
+ status = "okay";
+
+ panel@0 {
+ compatible = "fairphone,fp4-hx83112a-djn", "himax,hx83112a";
+ reg = <0>;
+
+ backlight = <&pm6150l_wled>;
+ reset-gpios = <&pm6150l_gpios 9 GPIO_ACTIVE_LOW>;
+
+ vdd1-supply = <&vreg_l1e>;
+ vsn-supply = <&lcdb_dummy>;
+ vsp-supply = <&lcdb_dummy>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&mdss_dsi0_out>;
+ };
+ };
+ };
+};
+
+&mdss_dsi0_out {
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&panel_in>;
+};
+
+&mdss_dsi0_phy {
+ vdds-supply = <&vreg_l18a>;
+ status = "okay";
+};
+
&mpss {
firmware-name = "qcom/sm7225/fairphone4/modem.mdt";
status = "okay";
--
2.43.0
Add support for the 2340x1080 LCD DJN panel bundled with a HX83112A
driver IC, as found on the Fairphone 4 smartphone.
Signed-off-by: Luca Weiss <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 9 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-himax-hx83112a.c | 372 +++++++++++++++++++++++++++
3 files changed, 382 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 99e14dc212ec..e98d329826f2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -162,6 +162,15 @@ config DRM_PANEL_FEIYANG_FY07024DI26A30D
Say Y if you want to enable support for panels based on the
Feiyang FY07024DI26A30-D MIPI-DSI interface.
+config DRM_PANEL_HIMAX_HX83112A
+ tristate "Himax HX83112A-based DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for Himax HX83112A-based
+ display panels, such as the one found in the Fairphone 4 smartphone.
+
config DRM_PANEL_HIMAX_HX8394
tristate "HIMAX HX8394 MIPI-DSI LCD panels"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index d10c3de51c6d..c2fc4c8c8340 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
+obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
new file mode 100644
index 000000000000..ce86aad11bf5
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
+ * Copyright (c) 2024 Luca Weiss <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct hx83112a_panel {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data supplies[3];
+ struct gpio_desc *reset_gpio;
+ bool prepared;
+};
+
+static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct hx83112a_panel, panel);
+}
+
+static void hx83112a_reset(struct hx83112a_panel *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ msleep(20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ msleep(50);
+}
+
+static int hx83112a_on(struct hx83112a_panel *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x83, 0x11, 0x2a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb1,
+ 0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
+ mipi_dsi_dcs_write_seq(dsi, 0xb2,
+ 0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
+ 0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
+ mipi_dsi_dcs_write_seq(dsi, 0xb4,
+ 0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
+ 0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
+ 0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
+ 0x12, 0x00, 0x29);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
+ mipi_dsi_dcs_write_seq(dsi, 0xb4,
+ 0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
+ 0x53);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x03);
+ mipi_dsi_dcs_write_seq(dsi, 0xc1,
+ 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
+ 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
+ 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
+ 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
+ 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
+ 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
+ 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
+ 0x40);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
+ mipi_dsi_dcs_write_seq(dsi, 0xc1,
+ 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
+ 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
+ 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
+ 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
+ 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
+ 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
+ 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
+ 0x40);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xc1,
+ 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
+ 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
+ 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
+ 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
+ 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
+ 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
+ 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
+ 0x40);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xc7, 0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x08);
+ mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x2b, 0x2b);
+ mipi_dsi_dcs_write_seq(dsi, 0xd3,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
+ 0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
+ 0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
+ 0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
+ 0x0f);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xd3,
+ 0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xd5,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
+ 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
+ 0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
+ 0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
+ mipi_dsi_dcs_write_seq(dsi, 0xd6,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
+ 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
+ 0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
+ 0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
+ mipi_dsi_dcs_write_seq(dsi, 0xd8,
+ 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xd8,
+ 0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
+ 0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
+ mipi_dsi_dcs_write_seq(dsi, 0xd8,
+ 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
+ 0xff, 0xff, 0xff, 0xff);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x03);
+ mipi_dsi_dcs_write_seq(dsi, 0xd8,
+ 0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xe7,
+ 0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
+ 0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
+ 0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xe7,
+ 0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
+ mipi_dsi_dcs_write_seq(dsi, 0xe7,
+ 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xe9, 0xc3);
+ mipi_dsi_dcs_write_seq(dsi, 0xcb, 0xd1, 0xd6);
+ mipi_dsi_dcs_write_seq(dsi, 0xe9, 0x3f);
+ mipi_dsi_dcs_write_seq(dsi, 0xe9, 0xc6);
+ mipi_dsi_dcs_write_seq(dsi, 0xbf, 0x37);
+ mipi_dsi_dcs_write_seq(dsi, 0xe9, 0x3f);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(150);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set display on: %d\n", ret);
+ return ret;
+ }
+ msleep(50);
+
+ return 0;
+}
+
+static int hx83112a_disable(struct drm_panel *panel)
+{
+ struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set display off: %d\n", ret);
+ return ret;
+ }
+ msleep(20);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(120);
+
+ return 0;
+}
+
+static int hx83112a_prepare(struct drm_panel *panel)
+{
+ struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ hx83112a_reset(ctx);
+
+ ret = hx83112a_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ return ret;
+ }
+
+ ctx->prepared = true;
+ return 0;
+}
+
+static int hx83112a_unprepare(struct drm_panel *panel)
+{
+ struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
+
+ if (!ctx->prepared)
+ return 0;
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static const struct drm_display_mode hx83112a_mode = {
+ .clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 28,
+ .hsync_end = 1080 + 28 + 8,
+ .htotal = 1080 + 28 + 8 + 8,
+ .vdisplay = 2340,
+ .vsync_start = 2340 + 27,
+ .vsync_end = 2340 + 27 + 5,
+ .vtotal = 2340 + 27 + 5 + 5,
+ .width_mm = 65,
+ .height_mm = 115,
+};
+
+static int hx83112a_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &hx83112a_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs hx83112a_panel_funcs = {
+ .prepare = hx83112a_prepare,
+ .unprepare = hx83112a_unprepare,
+ .disable = hx83112a_disable,
+ .get_modes = hx83112a_get_modes,
+};
+
+static int hx83112a_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct hx83112a_panel *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->supplies[0].supply = "vdd1";
+ ctx->supplies[1].supply = "vsn";
+ ctx->supplies[2].supply = "vsp";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_VIDEO_HSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ ctx->panel.prepare_prev_first = true;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hx83112a_remove(struct mipi_dsi_device *dsi)
+{
+ struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id hx83112a_of_match[] = {
+ { .compatible = "fairphone,fp4-hx83112a-djn" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hx83112a_of_match);
+
+static struct mipi_dsi_driver hx83112a_driver = {
+ .probe = hx83112a_probe,
+ .remove = hx83112a_remove,
+ .driver = {
+ .name = "panel-himax-hx83112a",
+ .of_match_table = hx83112a_of_match,
+ },
+};
+module_mipi_dsi_driver(hx83112a_driver);
+
+MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
+MODULE_LICENSE("GPL");
--
2.43.0
On Fri, 5 Jan 2024 at 16:29, Luca Weiss <[email protected]> wrote:
>
> Add support for the 2340x1080 LCD DJN panel bundled with a HX83112A
> driver IC, as found on the Fairphone 4 smartphone.
>
> Signed-off-by: Luca Weiss <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 9 +
> drivers/gpu/drm/panel/Makefile | 1 +
> drivers/gpu/drm/panel/panel-himax-hx83112a.c | 372 +++++++++++++++++++++++++++
> 3 files changed, 382 insertions(+)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 99e14dc212ec..e98d329826f2 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -162,6 +162,15 @@ config DRM_PANEL_FEIYANG_FY07024DI26A30D
> Say Y if you want to enable support for panels based on the
> Feiyang FY07024DI26A30-D MIPI-DSI interface.
>
> +config DRM_PANEL_HIMAX_HX83112A
> + tristate "Himax HX83112A-based DSI panel"
> + depends on OF
> + depends on DRM_MIPI_DSI
> + depends on BACKLIGHT_CLASS_DEVICE
> + help
> + Say Y here if you want to enable support for Himax HX83112A-based
> + display panels, such as the one found in the Fairphone 4 smartphone.
> +
> config DRM_PANEL_HIMAX_HX8394
> tristate "HIMAX HX8394 MIPI-DSI LCD panels"
> depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index d10c3de51c6d..c2fc4c8c8340 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
> obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
> obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
> obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
> +obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
> obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
> obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
> obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
> diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
> new file mode 100644
> index 000000000000..ce86aad11bf5
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c
> @@ -0,0 +1,372 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
> + * Copyright (c) 2024 Luca Weiss <[email protected]>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_panel.h>
> +
> +struct hx83112a_panel {
> + struct drm_panel panel;
> + struct mipi_dsi_device *dsi;
> + struct regulator_bulk_data supplies[3];
> + struct gpio_desc *reset_gpio;
> + bool prepared;
I think this should not be necessary after d2aacaf07395 ("drm/panel:
Check for already prepared/enabled in drm_panel")
> +};
> +
> +static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
> +{
> + return container_of(panel, struct hx83112a_panel, panel);
> +}
> +
> +static void hx83112a_reset(struct hx83112a_panel *ctx)
> +{
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + msleep(20);
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + msleep(20);
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + msleep(50);
> +}
> +
> +static int hx83112a_on(struct hx83112a_panel *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x83, 0x11, 0x2a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb1,
> + 0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
> + mipi_dsi_dcs_write_seq(dsi, 0xb2,
> + 0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
> + 0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
> + mipi_dsi_dcs_write_seq(dsi, 0xb4,
> + 0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
> + 0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
> + 0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
> + 0x12, 0x00, 0x29);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
> + mipi_dsi_dcs_write_seq(dsi, 0xb4,
> + 0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
> + 0x53);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x03);
> + mipi_dsi_dcs_write_seq(dsi, 0xc1,
> + 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
> + 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
> + 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
> + 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
> + 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
> + 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
> + 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
> + 0x40);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
> + mipi_dsi_dcs_write_seq(dsi, 0xc1,
> + 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
> + 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
> + 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
> + 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
> + 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
> + 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
> + 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
> + 0x40);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xc1,
> + 0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
> + 0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
> + 0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
> + 0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
> + 0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
> + 0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
> + 0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
> + 0x40);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xc7, 0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x08);
> + mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x2b, 0x2b);
> + mipi_dsi_dcs_write_seq(dsi, 0xd3,
> + 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
> + 0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
> + 0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
> + 0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
> + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
> + 0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
> + 0x0f);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xd3,
> + 0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xd5,
> + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
> + 0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
> + 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
> + 0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
> + 0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
> + 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
> + mipi_dsi_dcs_write_seq(dsi, 0xd6,
> + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
> + 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
> + 0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
> + 0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
> + 0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
> + 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
> + mipi_dsi_dcs_write_seq(dsi, 0xd8,
> + 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
> + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
> + 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xd8,
> + 0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
> + 0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
> + 0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
> + mipi_dsi_dcs_write_seq(dsi, 0xd8,
> + 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
> + 0xff, 0xff, 0xff, 0xff);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x03);
> + mipi_dsi_dcs_write_seq(dsi, 0xd8,
> + 0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
> + 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
> + 0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xe7,
> + 0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
> + 0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
> + 0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xe7,
> + 0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x02);
> + mipi_dsi_dcs_write_seq(dsi, 0xe7,
> + 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
> + 0x00, 0x00, 0x00, 0x02, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xe9, 0xc3);
> + mipi_dsi_dcs_write_seq(dsi, 0xcb, 0xd1, 0xd6);
> + mipi_dsi_dcs_write_seq(dsi, 0xe9, 0x3f);
> + mipi_dsi_dcs_write_seq(dsi, 0xe9, 0xc6);
> + mipi_dsi_dcs_write_seq(dsi, 0xbf, 0x37);
> + mipi_dsi_dcs_write_seq(dsi, 0xe9, 0x3f);
> +
> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(150);
> +
> + ret = mipi_dsi_dcs_set_display_on(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set display on: %d\n", ret);
> + return ret;
> + }
> + msleep(50);
> +
> + return 0;
> +}
> +
> +static int hx83112a_disable(struct drm_panel *panel)
> +{
> + struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_set_display_off(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set display off: %d\n", ret);
> + return ret;
> + }
> + msleep(20);
> +
> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(120);
> +
> + return 0;
> +}
> +
> +static int hx83112a_prepare(struct drm_panel *panel)
> +{
> + struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (ctx->prepared)
> + return 0;
> +
> + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable regulators: %d\n", ret);
> + return ret;
> + }
> +
> + hx83112a_reset(ctx);
> +
> + ret = hx83112a_on(ctx);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> + return ret;
> + }
> +
> + ctx->prepared = true;
> + return 0;
> +}
> +
> +static int hx83112a_unprepare(struct drm_panel *panel)
> +{
> + struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
> +
> + if (!ctx->prepared)
> + return 0;
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +
> + ctx->prepared = false;
> + return 0;
> +}
> +
> +static const struct drm_display_mode hx83112a_mode = {
> + .clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000,
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 28,
> + .hsync_end = 1080 + 28 + 8,
> + .htotal = 1080 + 28 + 8 + 8,
> + .vdisplay = 2340,
> + .vsync_start = 2340 + 27,
> + .vsync_end = 2340 + 27 + 5,
> + .vtotal = 2340 + 27 + 5 + 5,
> + .width_mm = 65,
> + .height_mm = 115,
> +};
> +
> +static int hx83112a_get_modes(struct drm_panel *panel,
> + struct drm_connector *connector)
> +{
> + struct drm_display_mode *mode;
> +
> + mode = drm_mode_duplicate(connector->dev, &hx83112a_mode);
> + if (!mode)
> + return -ENOMEM;
> +
> + drm_mode_set_name(mode);
> +
> + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> + connector->display_info.width_mm = mode->width_mm;
> + connector->display_info.height_mm = mode->height_mm;
> + drm_mode_probed_add(connector, mode);
This looks like drm_connector_helper_get_modes_fixed()
> +
> + return 1;
> +}
> +
> +static const struct drm_panel_funcs hx83112a_panel_funcs = {
> + .prepare = hx83112a_prepare,
> + .unprepare = hx83112a_unprepare,
> + .disable = hx83112a_disable,
> + .get_modes = hx83112a_get_modes,
> +};
> +
> +static int hx83112a_probe(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + struct hx83112a_panel *ctx;
> + int ret;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->supplies[0].supply = "vdd1";
> + ctx->supplies[1].supply = "vsn";
> + ctx->supplies[2].supply = "vsp";
> + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
> + ctx->supplies);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to get regulators\n");
> +
> + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(ctx->reset_gpio))
> + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
> + "Failed to get reset-gpios\n");
> +
> + ctx->dsi = dsi;
> + mipi_dsi_set_drvdata(dsi, ctx);
> +
> + dsi->lanes = 4;
> + dsi->format = MIPI_DSI_FMT_RGB888;
> + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
> + MIPI_DSI_MODE_VIDEO_HSE |
> + MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> + drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs,
> + DRM_MODE_CONNECTOR_DSI);
> + ctx->panel.prepare_prev_first = true;
> +
> + ret = drm_panel_of_backlight(&ctx->panel);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to get backlight\n");
> +
> + drm_panel_add(&ctx->panel);
> +
> + ret = mipi_dsi_attach(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
Probably this can become dev_err_probe() too.
> + drm_panel_remove(&ctx->panel);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void hx83112a_remove(struct mipi_dsi_device *dsi)
> +{
> + struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
> + int ret;
> +
> + ret = mipi_dsi_detach(dsi);
> + if (ret < 0)
> + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
> +
> + drm_panel_remove(&ctx->panel);
> +}
> +
> +static const struct of_device_id hx83112a_of_match[] = {
> + { .compatible = "fairphone,fp4-hx83112a-djn" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, hx83112a_of_match);
> +
> +static struct mipi_dsi_driver hx83112a_driver = {
> + .probe = hx83112a_probe,
> + .remove = hx83112a_remove,
> + .driver = {
> + .name = "panel-himax-hx83112a",
> + .of_match_table = hx83112a_of_match,
> + },
> +};
> +module_mipi_dsi_driver(hx83112a_driver);
> +
> +MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
> +MODULE_LICENSE("GPL");
>
> --
> 2.43.0
>
--
With best wishes
Dmitry
On 05/01/2024 15:29, Luca Weiss wrote:
> Himax HX83112A is a display driver IC used to drive LCD DSI panels.
> Describe it.
>
> Signed-off-by: Luca Weiss <[email protected]>
> ---
> .../bindings/display/panel/himax,hx83112a.yaml | 75 ++++++++++++++++++++++
> 1 file changed, 75 insertions(+)
>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
Best regards,
Krzysztof
On 1/5/24 15:29, Luca Weiss wrote:
> Add the description for the display panel found on this phone and remove
> the simple-framebuffer that was in place until now
Why? They should be able to coexist with a smooth-ish handoff
[...]
>
> +&gmu {
> + status = "okay";
Please kick the disablement from the SoC dtsi instead, it won't
probe without the GPU being enabled
Konrad
On Wed Jan 10, 2024 at 11:58 AM CET, Konrad Dybcio wrote:
>
>
> On 1/5/24 15:29, Luca Weiss wrote:
> > Add the description for the display panel found on this phone and remove
> > the simple-framebuffer that was in place until now
>
> Why? They should be able to coexist with a smooth-ish handoff
Does that work upstream? I'm aware that downstream can do this but
thought this was still missing upstream.
Didn't check what happens when you have both enabled.
>
> [...]
>
> >
> > +&gmu {
> > + status = "okay";
>
> Please kick the disablement from the SoC dtsi instead, it won't
> probe without the GPU being enabled
Ack, will include a patch in v2 for that.
>
> Konrad
On Wed, Jan 10, 2024 at 12:00:23PM +0100, Luca Weiss wrote:
> On Wed Jan 10, 2024 at 11:58 AM CET, Konrad Dybcio wrote:
> >
> >
> > On 1/5/24 15:29, Luca Weiss wrote:
> > > Add the description for the display panel found on this phone and remove
> > > the simple-framebuffer that was in place until now
> >
> > Why? They should be able to coexist with a smooth-ish handoff
>
> Does that work upstream? I'm aware that downstream can do this but
> thought this was still missing upstream.
It depends what you call smooth-ish I guess, but KMS handles the
handover just fine. You're likely to get a flicker during the transition
though.
Either way, the DT isn't the right place to choose, you should enable
both, and the distro will choose its policy through configuration.
Maxime
On 1/10/24 12:23, Maxime Ripard wrote:
> On Wed, Jan 10, 2024 at 12:00:23PM +0100, Luca Weiss wrote:
>> On Wed Jan 10, 2024 at 11:58 AM CET, Konrad Dybcio wrote:
>>>
>>>
>>> On 1/5/24 15:29, Luca Weiss wrote:
>>>> Add the description for the display panel found on this phone and remove
>>>> the simple-framebuffer that was in place until now
>>>
>>> Why? They should be able to coexist with a smooth-ish handoff
>>
>> Does that work upstream? I'm aware that downstream can do this but
>> thought this was still missing upstream.
>
> It depends what you call smooth-ish I guess, but KMS handles the
> handover just fine. You're likely to get a flicker during the transition
> though.
Yes, the panel driver will assert the reset pin to get the hw
into a predictable state, so there will likely be a split
second of black or black+garbage.
Konrad
On Wed Jan 10, 2024 at 12:23 PM CET, Maxime Ripard wrote:
> On Wed, Jan 10, 2024 at 12:00:23PM +0100, Luca Weiss wrote:
> > On Wed Jan 10, 2024 at 11:58 AM CET, Konrad Dybcio wrote:
> > >
> > >
> > > On 1/5/24 15:29, Luca Weiss wrote:
> > > > Add the description for the display panel found on this phone and remove
> > > > the simple-framebuffer that was in place until now
> > >
> > > Why? They should be able to coexist with a smooth-ish handoff
> >
> > Does that work upstream? I'm aware that downstream can do this but
> > thought this was still missing upstream.
>
> It depends what you call smooth-ish I guess, but KMS handles the
> handover just fine. You're likely to get a flicker during the transition
> though.
Right, seems to work. Also visually looks okay.
fairphone-fp4:~$ dmesg | grep "frame buffer device"
[ 0.250511] Console: switching to colour frame buffer device 135x146
[ 0.284146] simple-framebuffer a0000000.framebuffer: [drm] fb0: simpledrmdrmfb frame buffer device
[ 2.576712] Console: switching to colour frame buffer device 135x146
[ 2.604907] msm_dpu ae01000.display-controller: [drm] fb0: msmdrmfb frame buffer device
I thought before that having two outputs like this would cause two
different framebuffers/display devices to appear, but yeah as I said
seems okay. Will change in v2.
Regards
Luca
>
> Either way, the DT isn't the right place to choose, you should enable
> both, and the distro will choose its policy through configuration.
>
> Maxime