Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/panel: Add driver for DJN HX83112B LCD panel

Add support for the 2160x1080 LCD panel from DJN (98-03057-6598B-I)
bundled with a HX83112B driver IC, as found on the Fairphone 3
smartphone.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Luca Weiss <luca@lucaweiss.eu>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250611-fp3-display-v4-3-ef67701e7687@lucaweiss.eu

authored by

Luca Weiss and committed by
Neil Armstrong
df401fa1 54bd1390

+441
+10
drivers/gpu/drm/panel/Kconfig
··· 193 193 Say Y here if you want to enable support for Himax HX83112A-based 194 194 display panels, such as the one found in the Fairphone 4 smartphone. 195 195 196 + config DRM_PANEL_HIMAX_HX83112B 197 + tristate "Himax HX83112B-based DSI panel" 198 + depends on OF 199 + depends on DRM_MIPI_DSI 200 + depends on BACKLIGHT_CLASS_DEVICE 201 + select DRM_KMS_HELPER 202 + help 203 + Say Y here if you want to enable support for Himax HX83112B-based 204 + display panels, such as the one found in the Fairphone 3 smartphone. 205 + 196 206 config DRM_PANEL_HIMAX_HX8394 197 207 tristate "HIMAX HX8394 MIPI-DSI LCD panels" 198 208 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 20 20 obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) += panel-himax-hx8279.o 21 21 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) += panel-himax-hx83102.o 22 22 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o 23 + obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o 23 24 obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o 24 25 obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o 25 26 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
+430
drivers/gpu/drm/panel/panel-himax-hx83112b.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree. 4 + * Copyright (c) 2025 Luca Weiss <luca@lucaweiss.eu> 5 + */ 6 + 7 + #include <linux/backlight.h> 8 + #include <linux/delay.h> 9 + #include <linux/gpio/consumer.h> 10 + #include <linux/mod_devicetable.h> 11 + #include <linux/module.h> 12 + #include <linux/regulator/consumer.h> 13 + 14 + #include <video/mipi_display.h> 15 + 16 + #include <drm/drm_mipi_dsi.h> 17 + #include <drm/drm_modes.h> 18 + #include <drm/drm_panel.h> 19 + #include <drm/drm_probe_helper.h> 20 + 21 + /* Manufacturer specific DSI commands */ 22 + #define HX83112B_SETPOWER1 0xb1 23 + #define HX83112B_SETDISP 0xb2 24 + #define HX83112B_SETDRV 0xb4 25 + #define HX83112B_SETEXTC 0xb9 26 + #define HX83112B_SETBANK 0xbd 27 + #define HX83112B_SETDGCLUT 0xc1 28 + #define HX83112B_SETDISMO 0xc2 29 + #define HX83112B_UNKNOWN1 0xc6 30 + #define HX83112B_SETPANEL 0xcc 31 + #define HX83112B_UNKNOWN2 0xd1 32 + #define HX83112B_SETPOWER2 0xd2 33 + #define HX83112B_SETGIP0 0xd3 34 + #define HX83112B_SETGIP1 0xd5 35 + #define HX83112B_SETGIP2 0xd6 36 + #define HX83112B_SETGIP3 0xd8 37 + #define HX83112B_SETIDLE 0xdd 38 + #define HX83112B_UNKNOWN3 0xe7 39 + #define HX83112B_UNKNOWN4 0xe9 40 + 41 + struct hx83112b_panel { 42 + struct drm_panel panel; 43 + struct mipi_dsi_device *dsi; 44 + struct regulator_bulk_data *supplies; 45 + struct gpio_desc *reset_gpio; 46 + }; 47 + 48 + static const struct regulator_bulk_data hx83112b_supplies[] = { 49 + { .supply = "iovcc" }, 50 + { .supply = "vsn" }, 51 + { .supply = "vsp" }, 52 + }; 53 + 54 + static inline struct hx83112b_panel *to_hx83112b_panel(struct drm_panel *panel) 55 + { 56 + return container_of(panel, struct hx83112b_panel, panel); 57 + } 58 + 59 + static void hx83112b_reset(struct hx83112b_panel *ctx) 60 + { 61 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 62 + usleep_range(10000, 11000); 63 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 64 + usleep_range(10000, 11000); 65 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 66 + usleep_range(10000, 11000); 67 + } 68 + 69 + static int hx83112b_on(struct hx83112b_panel *ctx) 70 + { 71 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 72 + 73 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETEXTC, 0x83, 0x11, 0x2b); 74 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01); 75 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0x08, 0x70); 76 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03); 77 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x04, 0x38, 0x08, 0x70); 78 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 79 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER1, 80 + 0xf8, 0x27, 0x27, 0x00, 0x00, 0x0b, 0x0e, 81 + 0x0b, 0x0e, 0x33); 82 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPOWER2, 0x2d, 0x2d); 83 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 84 + 0x80, 0x02, 0x18, 0x80, 0x70, 0x00, 0x08, 85 + 0x1c, 0x08, 0x11, 0x05); 86 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd1); 87 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0x00, 0x08); 88 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 89 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02); 90 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISP, 0xb5, 0x0a); 91 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 92 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETIDLE, 93 + 0x00, 0x00, 0x08, 0x1c, 0x08, 0x34, 0x34, 94 + 0x88); 95 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV, 96 + 0x65, 0x6b, 0x00, 0x00, 0xd0, 0xd4, 0x36, 97 + 0xcf, 0x06, 0xce, 0x00, 0xce, 0x00, 0x00, 98 + 0x00, 0x07, 0x00, 0x2a, 0x07, 0x01, 0x07, 99 + 0x00, 0x00, 0x2a); 100 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03); 101 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc3); 102 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDRV, 0x01, 0x67, 0x2a); 103 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 104 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 105 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 0x01); 106 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01); 107 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 108 + 0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef, 109 + 0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda, 110 + 0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe, 111 + 0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85, 112 + 0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e, 113 + 0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07, 114 + 0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25, 115 + 0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b, 116 + 0x00); 117 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02); 118 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 119 + 0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef, 120 + 0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda, 121 + 0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe, 122 + 0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85, 123 + 0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e, 124 + 0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07, 125 + 0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25, 126 + 0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b, 127 + 0x00); 128 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03); 129 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDGCLUT, 130 + 0xff, 0xfb, 0xf9, 0xf6, 0xf4, 0xf1, 0xef, 131 + 0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdd, 0xda, 132 + 0xd8, 0xd5, 0xd2, 0xcf, 0xcc, 0xc5, 0xbe, 133 + 0xb7, 0xb0, 0xa8, 0xa0, 0x98, 0x8e, 0x85, 134 + 0x7b, 0x72, 0x69, 0x5e, 0x53, 0x48, 0x3e, 135 + 0x35, 0x2b, 0x22, 0x17, 0x0d, 0x09, 0x07, 136 + 0x05, 0x01, 0x00, 0x26, 0xf0, 0x86, 0x25, 137 + 0x6e, 0xb6, 0xdd, 0xf3, 0xd8, 0xcc, 0x9b, 138 + 0x00); 139 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 140 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETDISMO, 0xc8); 141 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETPANEL, 0x08); 142 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 143 + 0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 144 + 0x04, 0x00, 0x01, 0x13, 0x40, 0x04, 0x09, 145 + 0x09, 0x0b, 0x0b, 0x32, 0x10, 0x08, 0x00, 146 + 0x08, 0x32, 0x10, 0x08, 0x00, 0x08, 0x32, 147 + 0x10, 0x08, 0x00, 0x08, 0x00, 0x00, 0x0a, 148 + 0x08, 0x7b); 149 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc5); 150 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0xf7); 151 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 152 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xd4); 153 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN1, 0x6e); 154 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 155 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xef); 156 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0x0c); 157 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 158 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01); 159 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc8); 160 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP0, 0xa1); 161 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 162 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 163 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP1, 164 + 0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x18, 165 + 0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00, 166 + 0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18, 167 + 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 168 + 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35, 169 + 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18, 170 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xfc, 171 + 0xfc, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00); 172 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP2, 173 + 0x18, 0x18, 0x19, 0x18, 0x18, 0x20, 0x19, 174 + 0x18, 0x18, 0x10, 0x10, 0x18, 0x18, 0x00, 175 + 0x00, 0x18, 0x18, 0x01, 0x01, 0x18, 0x18, 176 + 0x28, 0x28, 0x18, 0x18, 0x18, 0x18, 0x18, 177 + 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x35, 178 + 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0x18, 179 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18); 180 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3, 181 + 0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 182 + 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa, 183 + 0xab, 0xaf, 0xef, 0xaa, 0xaa, 0xaa, 0xaa, 184 + 0xaf, 0xea, 0xaa); 185 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01); 186 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3, 187 + 0xaa, 0xaa, 0xab, 0xaf, 0xea, 0xaa, 0xaa, 188 + 0xaa, 0xae, 0xaf, 0xea, 0xaa); 189 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02); 190 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3, 191 + 0xaa, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 192 + 0xaa, 0xaa, 0xaf, 0xea, 0xaa); 193 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03); 194 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETGIP3, 195 + 0xba, 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 196 + 0xaa, 0xaa, 0xaf, 0xea, 0xaa, 0xba, 0xaa, 197 + 0xaa, 0xaf, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 198 + 0xaf, 0xea, 0xaa); 199 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 200 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xe4); 201 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x17, 0x69); 202 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 203 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 204 + 0x09, 0x09, 0x00, 0x07, 0xe8, 0x00, 0x26, 205 + 0x00, 0x07, 0x00, 0x00, 0xe8, 0x32, 0x00, 206 + 0xe9, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01, 207 + 0x01, 0x00, 0x12, 0x04); 208 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x01); 209 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 210 + 0x02, 0x00, 0x01, 0x20, 0x01, 0x18, 0x08, 211 + 0xa8, 0x09); 212 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x02); 213 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 0x20, 0x20, 0x00); 214 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x03); 215 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 216 + 0x00, 0xdc, 0x11, 0x70, 0x00, 0x20); 217 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0xc9); 218 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN3, 219 + 0x2a, 0xce, 0x02, 0x70, 0x01, 0x04); 220 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN4, 0x00); 221 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_SETBANK, 0x00); 222 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112B_UNKNOWN2, 0x27); 223 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 224 + mipi_dsi_msleep(&dsi_ctx, 120); 225 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 226 + mipi_dsi_msleep(&dsi_ctx, 20); 227 + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000); 228 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 229 + 0x24); 230 + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 231 + 232 + return dsi_ctx.accum_err; 233 + } 234 + 235 + static int hx83112b_off(struct hx83112b_panel *ctx) 236 + { 237 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 238 + 239 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 240 + mipi_dsi_msleep(&dsi_ctx, 20); 241 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 242 + mipi_dsi_msleep(&dsi_ctx, 120); 243 + 244 + return dsi_ctx.accum_err; 245 + } 246 + 247 + static int hx83112b_prepare(struct drm_panel *panel) 248 + { 249 + struct hx83112b_panel *ctx = to_hx83112b_panel(panel); 250 + struct device *dev = &ctx->dsi->dev; 251 + int ret; 252 + 253 + ret = regulator_bulk_enable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies); 254 + if (ret < 0) { 255 + dev_err(dev, "Failed to enable regulators: %d\n", ret); 256 + return ret; 257 + } 258 + 259 + hx83112b_reset(ctx); 260 + 261 + ret = hx83112b_on(ctx); 262 + if (ret < 0) { 263 + dev_err(dev, "Failed to initialize panel: %d\n", ret); 264 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 265 + regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies); 266 + return ret; 267 + } 268 + 269 + return 0; 270 + } 271 + 272 + static int hx83112b_unprepare(struct drm_panel *panel) 273 + { 274 + struct hx83112b_panel *ctx = to_hx83112b_panel(panel); 275 + struct device *dev = &ctx->dsi->dev; 276 + int ret; 277 + 278 + ret = hx83112b_off(ctx); 279 + if (ret < 0) 280 + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); 281 + 282 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 283 + regulator_bulk_disable(ARRAY_SIZE(hx83112b_supplies), ctx->supplies); 284 + 285 + return 0; 286 + } 287 + 288 + static const struct drm_display_mode hx83112b_mode = { 289 + .clock = (1080 + 40 + 4 + 12) * (2160 + 32 + 2 + 2) * 60 / 1000, 290 + .hdisplay = 1080, 291 + .hsync_start = 1080 + 40, 292 + .hsync_end = 1080 + 40 + 4, 293 + .htotal = 1080 + 40 + 4 + 12, 294 + .vdisplay = 2160, 295 + .vsync_start = 2160 + 32, 296 + .vsync_end = 2160 + 32 + 2, 297 + .vtotal = 2160 + 32 + 2 + 2, 298 + .width_mm = 65, 299 + .height_mm = 128, 300 + .type = DRM_MODE_TYPE_DRIVER, 301 + }; 302 + 303 + static int hx83112b_get_modes(struct drm_panel *panel, 304 + struct drm_connector *connector) 305 + { 306 + return drm_connector_helper_get_modes_fixed(connector, &hx83112b_mode); 307 + } 308 + 309 + static const struct drm_panel_funcs hx83112b_panel_funcs = { 310 + .prepare = hx83112b_prepare, 311 + .unprepare = hx83112b_unprepare, 312 + .get_modes = hx83112b_get_modes, 313 + }; 314 + 315 + static int hx83112b_bl_update_status(struct backlight_device *bl) 316 + { 317 + struct mipi_dsi_device *dsi = bl_get_data(bl); 318 + u16 brightness = backlight_get_brightness(bl); 319 + int ret; 320 + 321 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 322 + 323 + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 324 + if (ret < 0) 325 + return ret; 326 + 327 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 328 + 329 + return 0; 330 + } 331 + 332 + static const struct backlight_ops hx83112b_bl_ops = { 333 + .update_status = hx83112b_bl_update_status, 334 + }; 335 + 336 + static struct backlight_device * 337 + hx83112b_create_backlight(struct mipi_dsi_device *dsi) 338 + { 339 + struct device *dev = &dsi->dev; 340 + const struct backlight_properties props = { 341 + .type = BACKLIGHT_RAW, 342 + .brightness = 4095, 343 + .max_brightness = 4095, 344 + }; 345 + 346 + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 347 + &hx83112b_bl_ops, &props); 348 + } 349 + 350 + static int hx83112b_probe(struct mipi_dsi_device *dsi) 351 + { 352 + struct device *dev = &dsi->dev; 353 + struct hx83112b_panel *ctx; 354 + int ret; 355 + 356 + ctx = devm_drm_panel_alloc(dev, struct hx83112b_panel, panel, 357 + &hx83112b_panel_funcs, 358 + DRM_MODE_CONNECTOR_DSI); 359 + if (IS_ERR(ctx)) 360 + return PTR_ERR(ctx); 361 + 362 + ret = devm_regulator_bulk_get_const(dev, 363 + ARRAY_SIZE(hx83112b_supplies), 364 + hx83112b_supplies, 365 + &ctx->supplies); 366 + if (ret < 0) 367 + return ret; 368 + 369 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 370 + if (IS_ERR(ctx->reset_gpio)) 371 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 372 + "Failed to get reset-gpios\n"); 373 + 374 + ctx->dsi = dsi; 375 + mipi_dsi_set_drvdata(dsi, ctx); 376 + 377 + dsi->lanes = 4; 378 + dsi->format = MIPI_DSI_FMT_RGB888; 379 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 380 + MIPI_DSI_CLOCK_NON_CONTINUOUS | 381 + MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_LPM; 382 + 383 + ctx->panel.prepare_prev_first = true; 384 + 385 + ctx->panel.backlight = hx83112b_create_backlight(dsi); 386 + if (IS_ERR(ctx->panel.backlight)) 387 + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 388 + "Failed to create backlight\n"); 389 + 390 + drm_panel_add(&ctx->panel); 391 + 392 + ret = mipi_dsi_attach(dsi); 393 + if (ret < 0) { 394 + drm_panel_remove(&ctx->panel); 395 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 396 + } 397 + 398 + return 0; 399 + } 400 + 401 + static void hx83112b_remove(struct mipi_dsi_device *dsi) 402 + { 403 + struct hx83112b_panel *ctx = mipi_dsi_get_drvdata(dsi); 404 + int ret; 405 + 406 + ret = mipi_dsi_detach(dsi); 407 + if (ret < 0) 408 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 409 + 410 + drm_panel_remove(&ctx->panel); 411 + } 412 + 413 + static const struct of_device_id hx83112b_of_match[] = { 414 + { .compatible = "djn,98-03057-6598b-i" }, 415 + { /* sentinel */ } 416 + }; 417 + MODULE_DEVICE_TABLE(of, hx83112b_of_match); 418 + 419 + static struct mipi_dsi_driver hx83112b_driver = { 420 + .probe = hx83112b_probe, 421 + .remove = hx83112b_remove, 422 + .driver = { 423 + .name = "panel-himax-hx83112b", 424 + .of_match_table = hx83112b_of_match, 425 + }, 426 + }; 427 + module_mipi_dsi_driver(hx83112b_driver); 428 + 429 + MODULE_DESCRIPTION("DRM driver for hx83112b-equipped DSI panels"); 430 + MODULE_LICENSE("GPL");