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

drm: panel: Add support for Hydis HV101HD1 MIPI DSI panel

HV101HD1-1E1 is a color active matrix TFT LCD module using amorphous
silicon TFT's (Thin Film Transistors) as an active switching devices. This
module has a 10.1 inch diagonally measured active area with HD resolutions
(1366 horizontal by 768 vertical pixel array).

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250717135752.55958-3-clamor95@gmail.com

authored by

Svyatoslav Ryhel and committed by
Neil Armstrong
fdb4e289 bd068333

+202
+13
drivers/gpu/drm/panel/Kconfig
··· 215 215 216 216 If M is selected the module will be called panel-himax-hx8394. 217 217 218 + config DRM_PANEL_HYDIS_HV101HD1 219 + tristate "Hydis HV101HD1 panel" 220 + depends on OF 221 + depends on DRM_MIPI_DSI 222 + depends on BACKLIGHT_CLASS_DEVICE 223 + help 224 + Say Y here if you want to enable support for the Hydis HV101HD1 225 + 2-lane 1366x768 MIPI DSI panel found in ASUS VivoTab RT TF600T. 226 + HV101HD1 is a color active matrix TFT LCD module using amorphous 227 + silicon TFT's (Thin Film Transistors) as an active switching devices. 228 + 229 + If M is selected the module will be called panel-hydis-hv101hd1 230 + 218 231 config DRM_PANEL_ILITEK_IL9322 219 232 tristate "Ilitek ILI9322 320x240 QVGA panels" 220 233 depends on OF && SPI
+1
drivers/gpu/drm/panel/Makefile
··· 22 22 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o 23 23 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o 24 24 obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o 25 + obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o 25 26 obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o 26 27 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o 27 28 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o
+188
drivers/gpu/drm/panel/panel-hydis-hv101hd1.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/array_size.h> 4 + #include <linux/delay.h> 5 + #include <linux/err.h> 6 + #include <linux/gpio/consumer.h> 7 + #include <linux/mod_devicetable.h> 8 + #include <linux/module.h> 9 + #include <linux/property.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <video/mipi_display.h> 13 + 14 + #include <drm/drm_mipi_dsi.h> 15 + #include <drm/drm_modes.h> 16 + #include <drm/drm_panel.h> 17 + 18 + struct hv101hd1 { 19 + struct drm_panel panel; 20 + struct mipi_dsi_device *dsi; 21 + struct regulator_bulk_data *supplies; 22 + }; 23 + 24 + static const struct regulator_bulk_data hv101hd1_supplies[] = { 25 + { .supply = "vdd" }, 26 + { .supply = "vio" }, 27 + }; 28 + 29 + static inline struct hv101hd1 *to_hv101hd1(struct drm_panel *panel) 30 + { 31 + return container_of(panel, struct hv101hd1, panel); 32 + } 33 + 34 + static int hv101hd1_prepare(struct drm_panel *panel) 35 + { 36 + struct hv101hd1 *hv = to_hv101hd1(panel); 37 + struct mipi_dsi_multi_context ctx = { .dsi = hv->dsi }; 38 + struct device *dev = &hv->dsi->dev; 39 + int ret; 40 + 41 + ret = regulator_bulk_enable(ARRAY_SIZE(hv101hd1_supplies), hv->supplies); 42 + if (ret) { 43 + dev_err(dev, "error enabling regulators (%d)\n", ret); 44 + return ret; 45 + } 46 + 47 + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); 48 + mipi_dsi_msleep(&ctx, 20); 49 + 50 + mipi_dsi_dcs_set_display_on_multi(&ctx); 51 + mipi_dsi_msleep(&ctx, 20); 52 + 53 + return 0; 54 + } 55 + 56 + static int hv101hd1_disable(struct drm_panel *panel) 57 + { 58 + struct hv101hd1 *hv = to_hv101hd1(panel); 59 + struct mipi_dsi_multi_context ctx = { .dsi = hv->dsi }; 60 + 61 + mipi_dsi_dcs_set_display_off_multi(&ctx); 62 + mipi_dsi_msleep(&ctx, 120); 63 + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); 64 + mipi_dsi_msleep(&ctx, 20); 65 + 66 + return 0; 67 + } 68 + 69 + static int hv101hd1_unprepare(struct drm_panel *panel) 70 + { 71 + struct hv101hd1 *hv = to_hv101hd1(panel); 72 + 73 + return regulator_bulk_disable(ARRAY_SIZE(hv101hd1_supplies), 74 + hv->supplies); 75 + } 76 + 77 + static const struct drm_display_mode hv101hd1_mode = { 78 + .clock = (1366 + 74 + 36 + 24) * (768 + 21 + 7 + 4) * 60 / 1000, 79 + .hdisplay = 1366, 80 + .hsync_start = 1366 + 74, 81 + .hsync_end = 1366 + 74 + 36, 82 + .htotal = 1366 + 74 + 36 + 24, 83 + .vdisplay = 768, 84 + .vsync_start = 768 + 21, 85 + .vsync_end = 768 + 21 + 7, 86 + .vtotal = 768 + 21 + 7 + 4, 87 + .width_mm = 140, 88 + .height_mm = 220, 89 + }; 90 + 91 + static int hv101hd1_get_modes(struct drm_panel *panel, struct drm_connector *connector) 92 + { 93 + struct drm_display_mode *mode; 94 + 95 + mode = drm_mode_duplicate(connector->dev, &hv101hd1_mode); 96 + if (!mode) 97 + return -ENOMEM; 98 + 99 + drm_mode_set_name(mode); 100 + 101 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 102 + 103 + connector->display_info.width_mm = mode->width_mm; 104 + connector->display_info.height_mm = mode->height_mm; 105 + 106 + drm_mode_probed_add(connector, mode); 107 + 108 + return 1; 109 + } 110 + 111 + static const struct drm_panel_funcs hv101hd1_panel_funcs = { 112 + .prepare = hv101hd1_prepare, 113 + .disable = hv101hd1_disable, 114 + .unprepare = hv101hd1_unprepare, 115 + .get_modes = hv101hd1_get_modes, 116 + }; 117 + 118 + static int hv101hd1_probe(struct mipi_dsi_device *dsi) 119 + { 120 + struct device *dev = &dsi->dev; 121 + struct hv101hd1 *hv; 122 + int ret; 123 + 124 + hv = devm_drm_panel_alloc(dev, struct hv101hd1, panel, 125 + &hv101hd1_panel_funcs, 126 + DRM_MODE_CONNECTOR_DSI); 127 + if (IS_ERR(hv)) 128 + return PTR_ERR(hv); 129 + 130 + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(hv101hd1_supplies), 131 + hv101hd1_supplies, &hv->supplies); 132 + if (ret) 133 + return dev_err_probe(dev, ret, "failed to get regulators\n"); 134 + 135 + hv->dsi = dsi; 136 + mipi_dsi_set_drvdata(dsi, hv); 137 + 138 + dsi->lanes = 2; 139 + dsi->format = MIPI_DSI_FMT_RGB888; 140 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; 141 + 142 + ret = drm_panel_of_backlight(&hv->panel); 143 + if (ret) 144 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 145 + 146 + drm_panel_add(&hv->panel); 147 + 148 + ret = mipi_dsi_attach(dsi); 149 + if (ret) { 150 + drm_panel_remove(&hv->panel); 151 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 152 + } 153 + 154 + return 0; 155 + } 156 + 157 + static void hv101hd1_remove(struct mipi_dsi_device *dsi) 158 + { 159 + struct hv101hd1 *hv = mipi_dsi_get_drvdata(dsi); 160 + int ret; 161 + 162 + ret = mipi_dsi_detach(dsi); 163 + if (ret < 0) 164 + dev_err(&dsi->dev, 165 + "Failed to detach from DSI host: %d\n", ret); 166 + 167 + drm_panel_remove(&hv->panel); 168 + } 169 + 170 + static const struct of_device_id hv101hd1_of_match[] = { 171 + { .compatible = "hydis,hv101hd1" }, 172 + { /* sentinel */ } 173 + }; 174 + MODULE_DEVICE_TABLE(of, hv101hd1_of_match); 175 + 176 + static struct mipi_dsi_driver hv101hd1_driver = { 177 + .driver = { 178 + .name = "panel-hv101hd1", 179 + .of_match_table = hv101hd1_of_match, 180 + }, 181 + .probe = hv101hd1_probe, 182 + .remove = hv101hd1_remove, 183 + }; 184 + module_mipi_dsi_driver(hv101hd1_driver); 185 + 186 + MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 187 + MODULE_DESCRIPTION("DRM driver for Hydis HV101HD1 panel"); 188 + MODULE_LICENSE("GPL");