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

drm: panel: Add driver for Himax HX8279 DDIC panels

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 and for the Aoly
SL101PM1794FOG-v15 10.1" 1200x1920 LCD panel found on some I.MX8MM
boards.

Link: https://lore.kernel.org/r/20250410072456.387562-4-angelogioacchino.delregno@collabora.com
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250414082918.30298-4-angelogioacchino.delregno@collabora.com
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250414082918.30298-4-angelogioacchino.delregno@collabora.com

authored by

AngeloGioacchino Del Regno and committed by
Neil Armstrong
38d42c26 a424c93d

+1310
+11
drivers/gpu/drm/panel/Kconfig
··· 154 154 handling of power supplies or control signals. It implements automatic 155 155 backlight handling if the panel is attached to a backlight controller. 156 156 157 + config DRM_PANEL_HIMAX_HX8279 158 + tristate "Himax HX8279-based panels" 159 + depends on OF 160 + depends on DRM_MIPI_DSI 161 + depends on BACKLIGHT_CLASS_DEVICE 162 + help 163 + Say Y if you want to enable support for panels based on the 164 + Himax HX8279 controller, such as the Startek KD070FHFID078 165 + 7.0" 1200x1920 IPS LCD panel that uses a MIPI-DSI interface 166 + and others. 167 + 157 168 config DRM_PANEL_HIMAX_HX83102 158 169 tristate "Himax HX83102-based panels" 159 170 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 16 16 obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o 17 17 obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o 18 18 obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o 19 + obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) += panel-himax-hx8279.o 19 20 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) += panel-himax-hx83102.o 20 21 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o 21 22 obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
+1298
drivers/gpu/drm/panel/panel-himax-hx8279.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Himax HX8279 DriverIC panels driver 4 + * 5 + * Copyright (c) 2025 Collabora Ltd. 6 + * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/delay.h> 11 + #include <linux/gpio/consumer.h> 12 + #include <linux/module.h> 13 + #include <linux/of.h> 14 + #include <linux/of_graph.h> 15 + #include <linux/regulator/consumer.h> 16 + 17 + #include <drm/drm_connector.h> 18 + #include <drm/drm_crtc.h> 19 + #include <drm/drm_mipi_dsi.h> 20 + #include <drm/drm_modes.h> 21 + #include <drm/drm_panel.h> 22 + 23 + /* Page selection */ 24 + #define HX8279_REG_PAGE 0xb0 25 + #define HX8279_PAGE_SEL GENMASK(3, 0) 26 + 27 + /* Page 0 - Driver/Module Configuration */ 28 + #define HX8279_P0_VGHS 0xbf 29 + #define HX8279_P0_VGLS 0xc0 30 + #define HX8279_P0_VGPHS 0xc2 31 + #define HX8279_P0_VGNHS 0xc4 32 + #define HX8279_P0_VG_SEL GENMASK(4, 0) 33 + #define HX8279_VGH_MIN_MV 8700 34 + #define HX8279_VGH_STEP_MV 300 35 + #define HX8279_VGL_MIN_MV 6700 36 + #define HX8279_VGL_STEP_MV 300 37 + #define HX8279_VGPNH_MIN_MV 4000 38 + #define HX8279_VGPNX_STEP_MV 50 39 + #define HX8279_VGH_VOLT_SEL(x) ((x - HX8279_VGH_MIN_MV) / HX8279_VGH_STEP_MV) 40 + #define HX8279_VGL_VOLT_SEL(x) ((x - HX8279_VGL_MIN_MV) / HX8279_VGL_STEP_MV) 41 + #define HX8279_VGPN_VOLT_SEL(x) ((x - HX8279_VGPNH_MIN_MV) / HX8279_VGPNX_STEP_MV) 42 + 43 + /* Page 1 - Gate driver On Array (GOA) Mux */ 44 + #define HX8279_P1_REG_GOA_L 0xc0 45 + #define HX8279_P1_REG_GOUTL(x) (HX8279_P1_REG_GOA_L + (x)) 46 + #define HX8279_P1_REG_GOA_R 0xd4 47 + #define HX8279_P1_REG_GOUTR(x) (HX8279_P1_REG_GOA_R + (x)) 48 + #define HX8279_GOUT_STB GENMASK(7, 6) 49 + #define HX8279_GOUT_SEL GENMASK(5, 0) 50 + 51 + /* Page 2 - Analog Gamma Configuration */ 52 + #define HX8279_P2_REG_ANALOG_GAMMA 0xc0 53 + #define HX8279_P2_REG_GAMMA_T_PVP(x) (HX8279_P2_REG_ANALOG_GAMMA + (x)) /* 0..16 */ 54 + #define HX8279_P2_REG_GAMMA_T_PVN(x) (HX8279_P2_REG_GAMMA_T_PVP(17) + (x)) /* 0..16 */ 55 + 56 + /* Page 3 - Gate driver On Array (GOA) Configuration */ 57 + #define HX8279_P3_REG_UNKNOWN_BA 0xba 58 + #define HX8279_P3_REG_GOA_CKV_FALL_PREC 0xbc 59 + #define HX8279_P3_REG_GOA_TIMING_ODD 0xc2 60 + #define HX8279_P3_REG_GOA_TO(x) (HX8279_P3_REG_GOA_TIMING_ODD + x) /* GOA_T0..5 */ 61 + #define HX8279_P3_REG_GOA_STVL 0xc8 62 + #define HX8279_P3_GOA_STV_LEAD GENMASK(4, 0) 63 + #define HX8279_P3_REG_GOA_CKVL 0xc9 64 + #define HX8279_P3_GOA_CKV_LEAD GENMASK(4, 0) 65 + #define HX8279_P3_REG_GOA_CKVD 0xca 66 + #define HX8279_P3_GOA_CKV_NONOVERLAP BIT(7) 67 + #define HX8279_P3_GOA_CKV_RESERVED BIT(6) 68 + #define HX8279_P3_GOA_CKV_DUMMY GENMASK(5, 0) 69 + #define HX8279_P3_REG_GOA_CKV_RISE_PREC 0xcb 70 + #define HX8279_P3_REG_GOA_CLR1_W_ADJ 0xd2 71 + #define HX8279_P3_REG_GOA_CLR234_W_ADJ 0xd3 72 + #define HX8279_P3_REG_GOA_CLR1_CFG 0xd4 73 + #define HX8279_P3_REG_GOA_CLR_CFG(x) (HX8279_P3_REG_GOA_CLR1_CFG + (x)) /* CLR1..4 */ 74 + #define HX8279_P3_GOA_CLR_CFG_POLARITY BIT(7) 75 + #define HX8279_P3_GOA_CLR_CFG_STARTPOS GENMASK(6, 0) 76 + #define HX8279_P3_REG_GOA_TIMING_EVEN 0xdd 77 + #define HX8279_P3_REG_GOA_TE(x) (HX8279_P3_REG_GOA_TIMING_EVEN + x) 78 + #define HX8279_P3_REG_UNKNOWN_E4 0xe4 79 + #define HX8279_P3_REG_UNKNOWN_E5 0xe5 80 + 81 + /* Page 5 - MIPI */ 82 + #define HX8279_P5_REG_TIMING 0xb3 83 + #define HX8279_P5_TIMING_THS_SETTLE GENMASK(7, 5) 84 + #define HX8279_P5_TIMING_LHS_SETTLE BIT(4) 85 + #define HX8279_P5_TIMING_TLPX GENMASK(3, 0) 86 + #define HX8279_P5_REG_UNKNOWN_B8 0xb8 87 + #define HX8279_P5_REG_UNKNOWN_BC 0xbc 88 + #define HX8279_P5_REG_UNKNOWN_D6 0xd6 89 + 90 + /* Page 6 - Engineer */ 91 + #define HX8279_P6_REG_ENGINEER_PWD 0xb8 92 + #define HX8279_P6_REG_INHOUSE_FUNC 0xc0 93 + #define HX8279_P6_ENG_UNLOCK_WORD 0xa5 94 + #define HX8279_P6_REG_GAMMA_CHOPPER 0xbc 95 + #define HX8279_P6_GAMMA_POCGM_CTL GENMASK(6, 4) 96 + #define HX8279_P6_GAMMA_POGCMD_CTL GENMASK(2, 0) 97 + #define HX8279_P6_REG_VOLT_ADJ 0xc7 98 + /* For VCCIFS and VCCS - 0: 1450, 1: 1500, 2: 1550, 3: 1600 uV */ 99 + #define HX8279_P6_VOLT_ADJ_VCCIFS GENMASK(3, 2) 100 + #define HX8279_P6_VOLT_ADJ_VCCS GENMASK(1, 0) 101 + #define HX8279_P6_REG_DLY_TIME_ADJ 0xd5 102 + 103 + /* Page 7...12 - Digital Gamma Adjustment */ 104 + #define HX8279_PG_DIGITAL_GAMMA 0xb1 105 + #define HX8279_DGAMMA_DGMA1_HI GENMASK(7, 6) 106 + #define HX8279_DGAMMA_DGMA2_HI GENMASK(5, 4) 107 + #define HX8279_DGAMMA_DGMA3_HI GENMASK(3, 2) 108 + #define HX8279_DGAMMA_DGMA4_HI GENMASK(1, 0) 109 + #define HX8279_PG_DGAMMA_NUM_LO_BYTES 24 110 + #define HX8279_PG_DGAMMA_NUM_HI_BYTES 6 111 + 112 + struct hx8279 { 113 + struct drm_panel panel; 114 + struct mipi_dsi_device *dsi[2]; 115 + struct regulator_bulk_data vregs[2]; 116 + struct gpio_desc *enable_gpio; 117 + struct gpio_desc *reset_gpio; 118 + const struct hx8279_panel_desc *desc; 119 + u8 last_page; 120 + bool skip_voltage_config; 121 + bool skip_goa_config; 122 + bool skip_goa_timing; 123 + bool skip_goa_even_timing; 124 + bool skip_mipi_timing; 125 + }; 126 + 127 + struct hx8279_panel_mode { 128 + const struct drm_display_mode mode; 129 + u8 bpc; 130 + bool is_video_mode; 131 + }; 132 + 133 + /** 134 + * struct hx8279_goa_mux - Gate driver On Array Muxer 135 + * @gout_l: Mux GOA signal to GOUT Left pin 136 + * @gout_r: Mux GOA signal to GOUT Right pin 137 + */ 138 + struct hx8279_goa_mux { 139 + u8 gout_l[20]; 140 + u8 gout_r[20]; 141 + }; 142 + 143 + /** 144 + * struct hx8279_analog_gamma - Analog Gamma Adjustment 145 + * @pos: Positive gamma op's input voltage, adjusted by VGP(H/L) 146 + * @neg: Negative gamma op's input voltage, adjusted by VGN(H/L) 147 + * 148 + * Analog Gamma correction is performed with 17+17 reference voltages, 149 + * changed with resistor streams, and defined with 17 register values 150 + * for positive and 17 for negative. 151 + * 152 + * Each register holds resistance values, in 8.5ohms per unit, for the 153 + * following gamma levels: 154 + * 0, 8, 16, 28, 40, 56, 80, 128, 176, 200, 216, 228, 240, 248, 252, 255. 155 + */ 156 + struct hx8279_analog_gamma { 157 + u8 pos[17]; 158 + u8 neg[17]; 159 + }; 160 + 161 + /** 162 + * struct hx8279_digital_gamma - Digital Gamma Adjustment 163 + * @r: Adjustment for red component 164 + * @g: Adjustment for green component 165 + * @b: Adjustment for blue component 166 + * 167 + * The layout of this structure follows the register layout to simplify 168 + * both the handling and the declaration of those values in the driver. 169 + * Gamma correction is internally done with a 24 segment piecewise 170 + * linear interpolation; those segments are defined with 24 ten bits 171 + * values of which: 172 + * - The LOW eight bits for the first 24 registers start at the first 173 + * register (at 0xb1) of the Digital Gamma Adjustment page; 174 + * - The HIGH two bits for each of the 24 registers are contained 175 + * in the last six registers; 176 + * - The last six registers contain four groups of two-bits HI values 177 + * for each of the first 24 registers, but in an inverted fashion, 178 + * this means that the first two bits relate to the last register 179 + * of a set of four. 180 + * 181 + * The 24 segments refer to the following gamma levels: 182 + * 0, 1, 3, 7, 11, 15, 23, 31, 47, 63, 95, 127, 128, 160, 183 + * 192, 208, 224, 232, 240, 244, 248, 252, 254, 255 184 + */ 185 + struct hx8279_digital_gamma { 186 + u8 r[HX8279_PG_DGAMMA_NUM_LO_BYTES + HX8279_PG_DGAMMA_NUM_HI_BYTES]; 187 + u8 g[HX8279_PG_DGAMMA_NUM_LO_BYTES + HX8279_PG_DGAMMA_NUM_HI_BYTES]; 188 + u8 b[HX8279_PG_DGAMMA_NUM_LO_BYTES + HX8279_PG_DGAMMA_NUM_HI_BYTES]; 189 + }; 190 + 191 + struct hx8279_panel_desc { 192 + const struct mipi_dsi_device_info dsi_info; 193 + const struct hx8279_panel_mode *mode_data; 194 + u8 num_lanes; 195 + u8 num_modes; 196 + 197 + /* Page 0 */ 198 + unsigned int vgh_mv; 199 + unsigned int vgl_mv; 200 + unsigned int vgph_mv; 201 + unsigned int vgnh_mv; 202 + 203 + /* Page 1 */ 204 + const struct hx8279_goa_mux *gmux; 205 + 206 + /* Page 2 */ 207 + const struct hx8279_analog_gamma *agamma; 208 + 209 + /* Page 3 */ 210 + u8 goa_unk_ba; 211 + u8 goa_odd_timing[6]; 212 + u8 goa_even_timing[6]; 213 + u8 goa_stv_lead_time_ck; 214 + u8 goa_ckv_lead_time_ck; 215 + u8 goa_ckv_dummy_vblank_num; 216 + u8 goa_ckv_rise_precharge; 217 + u8 goa_ckv_fall_precharge; 218 + bool goa_ckv_non_overlap_ctl; 219 + u8 goa_clr1_width_adj; 220 + u8 goa_clr234_width_adj; 221 + s8 goa_clr_polarity[4]; 222 + int goa_clr_start_pos[4]; 223 + u8 goa_unk_e4; 224 + u8 goa_unk_e5; 225 + 226 + /* Page 5 */ 227 + u8 bta_tlpx; 228 + bool lhs_settle_time_by_osc25; 229 + u8 ths_settle_time; 230 + u8 timing_unk_b8; 231 + u8 timing_unk_bc; 232 + u8 timing_unk_d6; 233 + 234 + /* Page 6 */ 235 + u8 gamma_ctl; 236 + u8 volt_adj; 237 + u8 src_delay_time_adj_ck; 238 + 239 + /* Page 7..12 */ 240 + const struct hx8279_digital_gamma *dgamma; 241 + }; 242 + 243 + static inline struct hx8279 *to_hx8279(struct drm_panel *panel) 244 + { 245 + return container_of(panel, struct hx8279, panel); 246 + } 247 + 248 + static void hx8279_set_page(struct hx8279 *hx, 249 + struct mipi_dsi_multi_context *dsi_ctx, u8 page) 250 + { 251 + const u8 cmd_set_page[] = { HX8279_REG_PAGE, page }; 252 + 253 + if (hx->last_page == page) 254 + return; 255 + 256 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_page, ARRAY_SIZE(cmd_set_page)); 257 + if (!dsi_ctx->accum_err) 258 + hx->last_page = page; 259 + } 260 + 261 + static void hx8279_set_module_config(struct hx8279 *hx, 262 + struct mipi_dsi_multi_context *dsi_ctx) 263 + { 264 + const struct hx8279_panel_desc *desc = hx->desc; 265 + u8 cmd_set_voltage[2]; 266 + 267 + if (hx->skip_voltage_config) 268 + return; 269 + 270 + /* Page 0 - Driver/Module Configuration */ 271 + hx8279_set_page(hx, dsi_ctx, 0); 272 + 273 + if (desc->vgh_mv) { 274 + cmd_set_voltage[0] = HX8279_P0_VGHS; 275 + cmd_set_voltage[1] = HX8279_VGH_VOLT_SEL(desc->vgh_mv); 276 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, 277 + ARRAY_SIZE(cmd_set_voltage)); 278 + } 279 + 280 + if (desc->vgl_mv) { 281 + cmd_set_voltage[0] = HX8279_P0_VGLS; 282 + cmd_set_voltage[1] = HX8279_VGL_VOLT_SEL(desc->vgl_mv); 283 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, 284 + ARRAY_SIZE(cmd_set_voltage)); 285 + } 286 + 287 + if (desc->vgph_mv) { 288 + cmd_set_voltage[0] = HX8279_P0_VGPHS; 289 + cmd_set_voltage[1] = HX8279_VGPN_VOLT_SEL(desc->vgph_mv); 290 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, 291 + ARRAY_SIZE(cmd_set_voltage)); 292 + } 293 + 294 + if (desc->vgnh_mv) { 295 + cmd_set_voltage[0] = HX8279_P0_VGNHS; 296 + cmd_set_voltage[1] = HX8279_VGPN_VOLT_SEL(desc->vgnh_mv); 297 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_voltage, 298 + ARRAY_SIZE(cmd_set_voltage)); 299 + } 300 + } 301 + 302 + static void hx8279_set_gmux(struct hx8279 *hx, 303 + struct mipi_dsi_multi_context *dsi_ctx) 304 + { 305 + const struct hx8279_goa_mux *gmux = hx->desc->gmux; 306 + u8 cmd_set_gmux[2]; 307 + int i; 308 + 309 + if (!gmux) 310 + return; 311 + 312 + hx8279_set_page(hx, dsi_ctx, 1); 313 + 314 + for (i = 0; i < ARRAY_SIZE(gmux->gout_l); i++) { 315 + cmd_set_gmux[0] = HX8279_P1_REG_GOUTL(i); 316 + cmd_set_gmux[1] = gmux->gout_l[i]; 317 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gmux, 318 + ARRAY_SIZE(cmd_set_gmux)); 319 + } 320 + 321 + for (i = 0; i < ARRAY_SIZE(gmux->gout_r); i++) { 322 + cmd_set_gmux[0] = HX8279_P1_REG_GOUTR(i); 323 + cmd_set_gmux[1] = gmux->gout_r[i]; 324 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gmux, 325 + ARRAY_SIZE(cmd_set_gmux)); 326 + } 327 + } 328 + 329 + static void hx8279_set_analog_gamma(struct hx8279 *hx, 330 + struct mipi_dsi_multi_context *dsi_ctx) 331 + { 332 + const struct hx8279_analog_gamma *agamma = hx->desc->agamma; 333 + u8 cmd_set_ana_gamma[2]; 334 + int i; 335 + 336 + if (!agamma) 337 + return; 338 + 339 + hx8279_set_page(hx, dsi_ctx, 2); 340 + 341 + for (i = 0; i < ARRAY_SIZE(agamma->pos); i++) { 342 + cmd_set_ana_gamma[0] = HX8279_P2_REG_GAMMA_T_PVP(i); 343 + cmd_set_ana_gamma[1] = agamma->pos[i]; 344 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_ana_gamma, 345 + ARRAY_SIZE(cmd_set_ana_gamma)); 346 + } 347 + 348 + for (i = 0; i < ARRAY_SIZE(agamma->neg); i++) { 349 + cmd_set_ana_gamma[0] = HX8279_P2_REG_GAMMA_T_PVN(i); 350 + cmd_set_ana_gamma[1] = agamma->neg[i]; 351 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_ana_gamma, 352 + ARRAY_SIZE(cmd_set_ana_gamma)); 353 + } 354 + } 355 + 356 + static void hx8279_set_goa_timing(struct hx8279 *hx, 357 + struct mipi_dsi_multi_context *dsi_ctx) 358 + { 359 + const struct hx8279_panel_desc *desc = hx->desc; 360 + u8 cmd_set_goa_t[2]; 361 + int i; 362 + 363 + if (hx->skip_goa_timing) 364 + return; 365 + 366 + hx8279_set_page(hx, dsi_ctx, 3); 367 + 368 + for (i = 0; i < ARRAY_SIZE(desc->goa_odd_timing); i++) { 369 + cmd_set_goa_t[0] = HX8279_P3_REG_GOA_TO(i); 370 + cmd_set_goa_t[1] = desc->goa_odd_timing[i]; 371 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa_t, 372 + ARRAY_SIZE(cmd_set_goa_t)); 373 + } 374 + 375 + for (i = 0; i < ARRAY_SIZE(desc->goa_even_timing); i++) { 376 + cmd_set_goa_t[0] = HX8279_P3_REG_GOA_TE(i); 377 + cmd_set_goa_t[1] = desc->goa_odd_timing[i]; 378 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa_t, 379 + ARRAY_SIZE(cmd_set_goa_t)); 380 + } 381 + } 382 + 383 + static void hx8279_set_goa_cfg(struct hx8279 *hx, 384 + struct mipi_dsi_multi_context *dsi_ctx) 385 + { 386 + const struct hx8279_panel_desc *desc = hx->desc; 387 + u8 cmd_set_goa[2]; 388 + int i; 389 + 390 + if (hx->skip_goa_config) 391 + return; 392 + 393 + hx8279_set_page(hx, dsi_ctx, 3); 394 + 395 + if (desc->goa_unk_ba) { 396 + cmd_set_goa[0] = HX8279_P3_REG_UNKNOWN_BA; 397 + cmd_set_goa[1] = desc->goa_unk_ba; 398 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 399 + ARRAY_SIZE(cmd_set_goa)); 400 + } 401 + 402 + if (desc->goa_stv_lead_time_ck) { 403 + cmd_set_goa[0] = HX8279_P3_REG_GOA_STVL; 404 + cmd_set_goa[1] = FIELD_PREP(HX8279_P3_GOA_STV_LEAD, 405 + desc->goa_stv_lead_time_ck); 406 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 407 + ARRAY_SIZE(cmd_set_goa)); 408 + } 409 + 410 + if (desc->goa_ckv_lead_time_ck) { 411 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CKVL; 412 + cmd_set_goa[1] = FIELD_PREP(HX8279_P3_GOA_CKV_DUMMY, 413 + desc->goa_ckv_lead_time_ck); 414 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 415 + ARRAY_SIZE(cmd_set_goa)); 416 + } 417 + 418 + if (desc->goa_ckv_dummy_vblank_num) { 419 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CKVD; 420 + cmd_set_goa[1] = FIELD_PREP(HX8279_P3_GOA_CKV_LEAD, 421 + desc->goa_ckv_dummy_vblank_num); 422 + cmd_set_goa[1] |= FIELD_PREP(HX8279_P3_GOA_CKV_NONOVERLAP, 423 + desc->goa_ckv_non_overlap_ctl); 424 + /* RESERVED must be always set */ 425 + cmd_set_goa[1] |= HX8279_P3_GOA_CKV_RESERVED; 426 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 427 + ARRAY_SIZE(cmd_set_goa)); 428 + } 429 + 430 + /* 431 + * One of the two being more than zero means that we want to write 432 + * both of them. Anyway, the register default is zero in this case. 433 + */ 434 + if (desc->goa_ckv_rise_precharge || desc->goa_ckv_fall_precharge) { 435 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CKV_RISE_PREC; 436 + cmd_set_goa[1] = desc->goa_ckv_rise_precharge; 437 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 438 + ARRAY_SIZE(cmd_set_goa)); 439 + 440 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CKV_FALL_PREC; 441 + cmd_set_goa[1] = desc->goa_ckv_fall_precharge; 442 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 443 + ARRAY_SIZE(cmd_set_goa)); 444 + } 445 + 446 + if (desc->goa_clr1_width_adj) { 447 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CLR1_W_ADJ; 448 + cmd_set_goa[1] = desc->goa_clr1_width_adj; 449 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 450 + ARRAY_SIZE(cmd_set_goa)); 451 + } 452 + 453 + if (desc->goa_clr234_width_adj) { 454 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CLR234_W_ADJ; 455 + cmd_set_goa[1] = desc->goa_clr234_width_adj; 456 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 457 + ARRAY_SIZE(cmd_set_goa)); 458 + } 459 + 460 + /* Polarity and Start Position arrays are of the same size */ 461 + for (i = 0; i < ARRAY_SIZE(desc->goa_clr_polarity); i++) { 462 + if (desc->goa_clr_polarity[i] < 0 || desc->goa_clr_start_pos[i] < 0) 463 + continue; 464 + 465 + cmd_set_goa[0] = HX8279_P3_REG_GOA_CLR_CFG(i); 466 + cmd_set_goa[1] = FIELD_PREP(HX8279_P3_GOA_CLR_CFG_STARTPOS, 467 + desc->goa_clr_start_pos[i]); 468 + cmd_set_goa[1] |= FIELD_PREP(HX8279_P3_GOA_CLR_CFG_POLARITY, 469 + desc->goa_clr_polarity[i]); 470 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 471 + ARRAY_SIZE(cmd_set_goa)); 472 + } 473 + 474 + if (desc->goa_unk_e4) { 475 + cmd_set_goa[0] = HX8279_P3_REG_UNKNOWN_E4; 476 + cmd_set_goa[1] = desc->goa_unk_e4; 477 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 478 + ARRAY_SIZE(cmd_set_goa)); 479 + } 480 + 481 + cmd_set_goa[0] = HX8279_P3_REG_UNKNOWN_E5; 482 + cmd_set_goa[1] = desc->goa_unk_e5; 483 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_goa, 484 + ARRAY_SIZE(cmd_set_goa)); 485 + } 486 + 487 + static void hx8279_set_mipi_cfg(struct hx8279 *hx, 488 + struct mipi_dsi_multi_context *dsi_ctx) 489 + { 490 + const struct hx8279_panel_desc *desc = hx->desc; 491 + u8 cmd_set_mipi[2]; 492 + 493 + if (hx->skip_mipi_timing) 494 + return; 495 + 496 + hx8279_set_page(hx, dsi_ctx, 5); 497 + 498 + if (desc->bta_tlpx || desc->ths_settle_time || desc->lhs_settle_time_by_osc25) { 499 + cmd_set_mipi[0] = HX8279_P5_REG_TIMING; 500 + cmd_set_mipi[1] = FIELD_PREP(HX8279_P5_TIMING_TLPX, desc->bta_tlpx); 501 + cmd_set_mipi[1] |= FIELD_PREP(HX8279_P5_TIMING_THS_SETTLE, 502 + desc->ths_settle_time); 503 + cmd_set_mipi[1] |= FIELD_PREP(HX8279_P5_TIMING_LHS_SETTLE, 504 + desc->lhs_settle_time_by_osc25); 505 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, 506 + ARRAY_SIZE(cmd_set_mipi)); 507 + } 508 + 509 + if (desc->timing_unk_b8) { 510 + cmd_set_mipi[0] = HX8279_P5_REG_UNKNOWN_B8; 511 + cmd_set_mipi[1] = desc->timing_unk_b8; 512 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, 513 + ARRAY_SIZE(cmd_set_mipi)); 514 + } 515 + 516 + if (desc->timing_unk_bc) { 517 + cmd_set_mipi[0] = HX8279_P5_REG_UNKNOWN_BC; 518 + cmd_set_mipi[1] = desc->timing_unk_bc; 519 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, 520 + ARRAY_SIZE(cmd_set_mipi)); 521 + } 522 + 523 + if (desc->timing_unk_d6) { 524 + cmd_set_mipi[0] = HX8279_P5_REG_UNKNOWN_D6; 525 + cmd_set_mipi[1] = desc->timing_unk_d6; 526 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_mipi, 527 + ARRAY_SIZE(cmd_set_mipi)); 528 + } 529 + } 530 + 531 + static void hx8279_set_adv_cfg(struct hx8279 *hx, 532 + struct mipi_dsi_multi_context *dsi_ctx) 533 + { 534 + const struct hx8279_panel_desc *desc = hx->desc; 535 + const u8 cmd_set_dly[] = { HX8279_P6_REG_DLY_TIME_ADJ, desc->src_delay_time_adj_ck }; 536 + const u8 cmd_set_gamma[] = { HX8279_P6_REG_GAMMA_CHOPPER, desc->gamma_ctl }; 537 + const u8 cmd_set_volt_adj[] = { HX8279_P6_REG_VOLT_ADJ, desc->volt_adj }; 538 + u8 cmd_set_eng[] = { HX8279_P6_REG_ENGINEER_PWD, HX8279_P6_ENG_UNLOCK_WORD }; 539 + 540 + if (!desc->gamma_ctl && !desc->src_delay_time_adj_ck && !desc->volt_adj) 541 + return; 542 + 543 + hx8279_set_page(hx, dsi_ctx, 6); 544 + 545 + /* Unlock ENG settings: write same word to both ENGINEER_PWD and INHOUSE_FUNC */ 546 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng)); 547 + 548 + cmd_set_eng[0] = HX8279_P6_REG_INHOUSE_FUNC; 549 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng)); 550 + 551 + /* Set Gamma Chopper and Gamma buffer Chopper control */ 552 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_gamma, ARRAY_SIZE(cmd_set_gamma)); 553 + 554 + /* Set Source delay time adjustment (CKV falling to Source off) */ 555 + if (desc->src_delay_time_adj_ck) 556 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_dly, 557 + ARRAY_SIZE(cmd_set_dly)); 558 + 559 + /* Set voltage adjustment */ 560 + if (desc->volt_adj) 561 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_volt_adj, 562 + ARRAY_SIZE(cmd_set_volt_adj)); 563 + 564 + /* Lock ENG settings again */ 565 + cmd_set_eng[0] = HX8279_P6_REG_ENGINEER_PWD; 566 + cmd_set_eng[1] = 0; 567 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng)); 568 + 569 + cmd_set_eng[0] = HX8279_P6_REG_INHOUSE_FUNC; 570 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_eng, ARRAY_SIZE(cmd_set_eng)); 571 + } 572 + 573 + static void hx8279_set_digital_gamma(struct hx8279 *hx, 574 + struct mipi_dsi_multi_context *dsi_ctx) 575 + { 576 + const struct hx8279_digital_gamma *dgamma = hx->desc->dgamma; 577 + u8 cmd_set_dig_gamma[2]; 578 + int i; 579 + 580 + if (!dgamma) 581 + return; 582 + 583 + /* 584 + * Pages 7..9 are for RGB Positive, 10..12 are for RGB Negative: 585 + * The first iteration sets all positive component registers, 586 + * the second one sets all negatives. 587 + */ 588 + for (i = 0; i < 2; i++) { 589 + u8 pg_neg = i * 3; 590 + 591 + hx8279_set_page(hx, dsi_ctx, 7 + pg_neg); 592 + 593 + for (i = 0; i < ARRAY_SIZE(dgamma->r); i++) { 594 + cmd_set_dig_gamma[0] = HX8279_PG_DIGITAL_GAMMA + i; 595 + cmd_set_dig_gamma[1] = dgamma->r[i]; 596 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_dig_gamma, 597 + ARRAY_SIZE(cmd_set_dig_gamma)); 598 + } 599 + 600 + hx8279_set_page(hx, dsi_ctx, 8 + pg_neg); 601 + 602 + for (i = 0; i < ARRAY_SIZE(dgamma->g); i++) { 603 + cmd_set_dig_gamma[0] = HX8279_PG_DIGITAL_GAMMA + i; 604 + cmd_set_dig_gamma[1] = dgamma->g[i]; 605 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_dig_gamma, 606 + ARRAY_SIZE(cmd_set_dig_gamma)); 607 + } 608 + 609 + hx8279_set_page(hx, dsi_ctx, 9 + pg_neg); 610 + 611 + for (i = 0; i < ARRAY_SIZE(dgamma->b); i++) { 612 + cmd_set_dig_gamma[0] = HX8279_PG_DIGITAL_GAMMA + i; 613 + cmd_set_dig_gamma[1] = dgamma->b[i]; 614 + mipi_dsi_generic_write_multi(dsi_ctx, cmd_set_dig_gamma, 615 + ARRAY_SIZE(cmd_set_dig_gamma)); 616 + } 617 + } 618 + } 619 + 620 + static int hx8279_on(struct hx8279 *hx) 621 + { 622 + struct mipi_dsi_device *dsi = hx->dsi[0]; 623 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 624 + 625 + /* Page 5 */ 626 + hx8279_set_mipi_cfg(hx, &dsi_ctx); 627 + 628 + /* Page 1 */ 629 + hx8279_set_gmux(hx, &dsi_ctx); 630 + 631 + /* Page 2 */ 632 + hx8279_set_analog_gamma(hx, &dsi_ctx); 633 + 634 + /* Page 3 */ 635 + hx8279_set_goa_cfg(hx, &dsi_ctx); 636 + hx8279_set_goa_timing(hx, &dsi_ctx); 637 + 638 + /* Page 0 - Driver/Module Configuration */ 639 + hx8279_set_module_config(hx, &dsi_ctx); 640 + 641 + /* Page 6 */ 642 + hx8279_set_adv_cfg(hx, &dsi_ctx); 643 + 644 + /* Pages 7..12 */ 645 + hx8279_set_digital_gamma(hx, &dsi_ctx); 646 + 647 + return dsi_ctx.accum_err; 648 + } 649 + 650 + static void hx8279_power_off(struct hx8279 *hx) 651 + { 652 + gpiod_set_value_cansleep(hx->reset_gpio, 0); 653 + usleep_range(100, 500); 654 + gpiod_set_value_cansleep(hx->enable_gpio, 0); 655 + regulator_bulk_disable(ARRAY_SIZE(hx->vregs), hx->vregs); 656 + } 657 + 658 + static int hx8279_disable(struct drm_panel *panel) 659 + { 660 + struct hx8279 *hx = to_hx8279(panel); 661 + struct mipi_dsi_device *dsi = hx->dsi[0]; 662 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 663 + 664 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 665 + 666 + return 0; 667 + } 668 + 669 + static int hx8279_enable(struct drm_panel *panel) 670 + { 671 + struct hx8279 *hx = to_hx8279(panel); 672 + struct mipi_dsi_device *dsi = hx->dsi[0]; 673 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 674 + 675 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 676 + 677 + return 0; 678 + } 679 + 680 + static int hx8279_prepare(struct drm_panel *panel) 681 + { 682 + struct hx8279 *hx = to_hx8279(panel); 683 + struct mipi_dsi_device *dsi = hx->dsi[0]; 684 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 685 + int ret; 686 + 687 + ret = regulator_bulk_enable(ARRAY_SIZE(hx->vregs), hx->vregs); 688 + if (ret) 689 + return ret; 690 + 691 + gpiod_set_value_cansleep(hx->enable_gpio, 1); 692 + usleep_range(5000, 6000); 693 + gpiod_set_value_cansleep(hx->reset_gpio, 1); 694 + usleep_range(6000, 7000); 695 + 696 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 697 + if (hx->dsi[1]) 698 + hx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM; 699 + 700 + ret = hx8279_on(hx); 701 + if (ret) { 702 + hx8279_power_off(hx); 703 + return ret; 704 + } 705 + 706 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 707 + mipi_dsi_msleep(&dsi_ctx, 130); 708 + 709 + return dsi_ctx.accum_err; 710 + } 711 + 712 + static int hx8279_unprepare(struct drm_panel *panel) 713 + { 714 + struct hx8279 *hx = to_hx8279(panel); 715 + struct mipi_dsi_device *dsi = hx->dsi[0]; 716 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 717 + 718 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 719 + mipi_dsi_msleep(&dsi_ctx, 130); 720 + 721 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 722 + if (hx->dsi[1]) 723 + hx->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM; 724 + 725 + hx8279_power_off(hx); 726 + 727 + return dsi_ctx.accum_err; 728 + } 729 + 730 + static int hx8279_get_modes(struct drm_panel *panel, struct drm_connector *connector) 731 + { 732 + struct hx8279 *hx = to_hx8279(panel); 733 + int i; 734 + 735 + for (i = 0; i < hx->desc->num_modes; i++) { 736 + struct drm_display_mode *mode; 737 + 738 + mode = drm_mode_duplicate(connector->dev, &hx->desc->mode_data[i].mode); 739 + if (!mode) 740 + return -ENOMEM; 741 + 742 + drm_mode_set_name(mode); 743 + 744 + mode->type |= DRM_MODE_TYPE_DRIVER; 745 + if (hx->desc->num_modes == 1) 746 + mode->type |= DRM_MODE_TYPE_PREFERRED; 747 + 748 + drm_mode_probed_add(connector, mode); 749 + } 750 + 751 + connector->display_info.bpc = hx->desc->mode_data[0].bpc; 752 + connector->display_info.height_mm = hx->desc->mode_data[0].mode.height_mm; 753 + connector->display_info.width_mm = hx->desc->mode_data[0].mode.width_mm; 754 + 755 + return hx->desc->num_modes; 756 + } 757 + 758 + static const struct drm_panel_funcs hx8279_panel_funcs = { 759 + .disable = hx8279_disable, 760 + .unprepare = hx8279_unprepare, 761 + .prepare = hx8279_prepare, 762 + .enable = hx8279_enable, 763 + .get_modes = hx8279_get_modes, 764 + }; 765 + 766 + static int hx8279_init_vregs(struct hx8279 *hx, struct device *dev) 767 + { 768 + int ret; 769 + 770 + hx->vregs[0].supply = "vdd"; 771 + hx->vregs[1].supply = "iovcc"; 772 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hx->vregs), 773 + hx->vregs); 774 + if (ret < 0) 775 + return ret; 776 + 777 + ret = regulator_is_supported_voltage(hx->vregs[0].consumer, 778 + 3000000, 5000000); 779 + if (!ret) 780 + return -EINVAL; 781 + 782 + ret = regulator_is_supported_voltage(hx->vregs[1].consumer, 783 + 1700000, 1900000); 784 + if (!ret) 785 + return -EINVAL; 786 + 787 + return 0; 788 + } 789 + 790 + static int hx8279_check_gmux_config(struct hx8279 *hx, struct device *dev) 791 + { 792 + const struct hx8279_panel_desc *desc = hx->desc; 793 + const struct hx8279_goa_mux *gmux = desc->gmux; 794 + int i; 795 + 796 + /* No gmux defined means we simply skip the GOA mux configuration */ 797 + if (!gmux) 798 + return 0; 799 + 800 + for (i = 0; i < ARRAY_SIZE(gmux->gout_l); i++) { 801 + if (gmux->gout_l[i] > (HX8279_GOUT_STB | HX8279_GOUT_SEL)) 802 + return dev_err_probe(dev, -EINVAL, 803 + "Invalid value found in gout_l[%d]\n", i); 804 + } 805 + 806 + for (i = 0; i < ARRAY_SIZE(gmux->gout_r); i++) { 807 + if (gmux->gout_r[i] > (HX8279_GOUT_STB | HX8279_GOUT_SEL)) 808 + return dev_err_probe(dev, -EINVAL, 809 + "Invalid value found in gout_r[%d]\n", i); 810 + } 811 + 812 + return 0; 813 + } 814 + 815 + static int hx8279_check_goa_config(struct hx8279 *hx, struct device *dev) 816 + { 817 + const struct hx8279_panel_desc *desc = hx->desc; 818 + bool goa_odd_valid, goa_even_valid; 819 + int i, num_zero, num_clr = 0; 820 + 821 + /* Up to 4 zero values is a valid configuration. Check them all. */ 822 + num_zero = 1; 823 + for (i = 0; i < ARRAY_SIZE(desc->goa_odd_timing); i++) { 824 + if (desc->goa_odd_timing[i]) 825 + num_zero++; 826 + } 827 + 828 + if (num_zero == ARRAY_SIZE(desc->goa_odd_timing)) 829 + goa_odd_valid = false; 830 + 831 + /* Up to 3 zeroes is a valid config. Check them all. */ 832 + num_zero = 1; 833 + for (i = 0; i < ARRAY_SIZE(desc->goa_even_timing); i++) { 834 + if (desc->goa_even_timing[i]) 835 + num_zero++; 836 + } 837 + 838 + if (num_zero == ARRAY_SIZE(desc->goa_even_timing)) 839 + goa_even_valid = false; 840 + 841 + /* Programming one without the other would make no sense! */ 842 + if (goa_odd_valid != goa_even_valid) 843 + return -EINVAL; 844 + 845 + /* We know that both are either true or false now, check just one */ 846 + if (!goa_odd_valid) 847 + hx->skip_goa_timing = true; 848 + 849 + if (!desc->goa_unk_ba && !desc->goa_stv_lead_time_ck && 850 + !desc->goa_ckv_lead_time_ck && !desc->goa_ckv_dummy_vblank_num && 851 + !desc->goa_ckv_rise_precharge && !desc->goa_ckv_fall_precharge && 852 + !desc->goa_clr1_width_adj && !desc->goa_clr234_width_adj && 853 + !desc->goa_unk_e4 && !desc->goa_unk_e5) { 854 + hx->skip_goa_config = true; 855 + return 0; 856 + } 857 + 858 + if ((desc->goa_stv_lead_time_ck > HX8279_P3_GOA_STV_LEAD) || 859 + (desc->goa_ckv_lead_time_ck > HX8279_P3_GOA_CKV_LEAD) || 860 + (desc->goa_ckv_dummy_vblank_num > HX8279_P3_GOA_CKV_DUMMY)) 861 + return dev_err_probe(dev, -EINVAL, 862 + "Invalid lead timings in GOA config\n"); 863 + 864 + /* 865 + * Don't perform zero check for polarity and start position, as 866 + * both pol=0 and start=0 are valid configuration values. 867 + */ 868 + for (i = 0; i < ARRAY_SIZE(desc->goa_clr_start_pos); i++) { 869 + if (desc->goa_clr_start_pos[i] < 0) 870 + continue; 871 + else if (desc->goa_clr_start_pos[i] > HX8279_P3_GOA_CLR_CFG_STARTPOS) 872 + return dev_err_probe(dev, -EINVAL, 873 + "Invalid start position for CLR%d\n", i + 1); 874 + else 875 + num_clr++; 876 + } 877 + if (!num_clr) 878 + return -EINVAL; 879 + 880 + for (i = 0; i < ARRAY_SIZE(desc->goa_clr_polarity); i++) { 881 + if (num_clr < 0) 882 + return -EINVAL; 883 + 884 + if (desc->goa_clr_polarity[i] < 0) 885 + continue; 886 + else if (desc->goa_clr_polarity[i] > 1) 887 + return dev_err_probe(dev, -EINVAL, 888 + "Invalid polarity for CLR%d\n", i + 1); 889 + else 890 + num_clr--; 891 + } 892 + 893 + return 0; 894 + } 895 + 896 + static int hx8279_check_dig_gamma(struct hx8279 *hx, struct device *dev, const u8 *component) 897 + { 898 + u8 gamma_high_bits[4]; 899 + u16 prev_val = 0; 900 + int i, j, k, x; 901 + 902 + /* 903 + * The gamma values are 10 bits long and shall be incremental 904 + * to form a digital gamma correction reference curve. 905 + * 906 + * As for the registers format: the first 24 bytes contain each the 907 + * lowest 8 bits for each of the gamma level references, and the last 908 + * 6 bytes contain the high two bits of 4 registers at a time, where 909 + * the first two bits are relative to the last register, and the last 910 + * two are relative to the first register. 911 + * 912 + * Another way of saying, those are the first four LOW values: 913 + * DGMA1_LO = 0xb1, DGMA2_LO = 0xb2, DGMA3_LO = 0xb3, DGMA4_LO = 0xb4 914 + * 915 + * The high values for those four are at DGMA1_4_HI = 0xc9; 916 + * ...and DGMA1_4_HI's data contains the following bits: 917 + * [1:0] = DGMA4_HI, [3:2] = DGMA3_HI, [5:4] = DGMA2_HI, [7:6] = DGMA1_HI 918 + */ 919 + for (i = 0; i < HX8279_PG_DGAMMA_NUM_HI_BYTES; i++) { 920 + k = HX8279_PG_DGAMMA_NUM_LO_BYTES + i; 921 + j = i * 4; 922 + x = 0; 923 + 924 + gamma_high_bits[0] = FIELD_GET(HX8279_DGAMMA_DGMA1_HI, component[k]); 925 + gamma_high_bits[1] = FIELD_GET(HX8279_DGAMMA_DGMA2_HI, component[k]); 926 + gamma_high_bits[2] = FIELD_GET(HX8279_DGAMMA_DGMA3_HI, component[k]); 927 + gamma_high_bits[3] = FIELD_GET(HX8279_DGAMMA_DGMA4_HI, component[k]); 928 + 929 + do { 930 + u16 cur_val = component[j] | (gamma_high_bits[x] << 8); 931 + 932 + if (cur_val < prev_val) 933 + return dev_err_probe(dev, -EINVAL, 934 + "Invalid dgamma values: %u < %u!\n", 935 + cur_val, prev_val); 936 + prev_val = cur_val; 937 + j++; 938 + x++; 939 + } while (x < 4); 940 + }; 941 + 942 + return 0; 943 + } 944 + 945 + static int hx8279_check_params(struct hx8279 *hx, struct device *dev) 946 + { 947 + const struct hx8279_panel_desc *desc = hx->desc; 948 + int ret; 949 + 950 + /* Voltages config validation */ 951 + if (!desc->vgh_mv && !desc->vgl_mv && !desc->vgph_mv && !desc->vgnh_mv) 952 + hx->skip_voltage_config = true; 953 + else if ((desc->vgh_mv && desc->vgh_mv < HX8279_VGH_MIN_MV) || 954 + (desc->vgl_mv && desc->vgl_mv < HX8279_VGL_MIN_MV) || 955 + (desc->vgph_mv && desc->vgph_mv < HX8279_VGPNH_MIN_MV) || 956 + (desc->vgnh_mv && desc->vgnh_mv < HX8279_VGPNH_MIN_MV)) 957 + return -EINVAL; 958 + 959 + /* GOA Muxing validation */ 960 + ret = hx8279_check_gmux_config(hx, dev); 961 + if (ret) 962 + return ret; 963 + 964 + /* GOA Configuration validation */ 965 + ret = hx8279_check_goa_config(hx, dev); 966 + if (ret) 967 + return ret; 968 + 969 + /* MIPI Configuration validation */ 970 + if (!desc->bta_tlpx && !desc->lhs_settle_time_by_osc25 && 971 + !desc->ths_settle_time && !desc->timing_unk_b8 && 972 + !desc->timing_unk_bc && !desc->timing_unk_d6) 973 + hx->skip_mipi_timing = true; 974 + 975 + /* ENG/Gamma Configuration validation */ 976 + if (desc->gamma_ctl > (HX8279_P6_GAMMA_POCGM_CTL | HX8279_P6_GAMMA_POGCMD_CTL)) 977 + return -EINVAL; 978 + 979 + /* Digital Gamma values validation */ 980 + if (desc->dgamma) { 981 + ret = hx8279_check_dig_gamma(hx, dev, desc->dgamma->r); 982 + if (ret) 983 + return ret; 984 + 985 + ret = hx8279_check_dig_gamma(hx, dev, desc->dgamma->g); 986 + if (ret) 987 + return ret; 988 + 989 + ret = hx8279_check_dig_gamma(hx, dev, desc->dgamma->b); 990 + if (ret) 991 + return ret; 992 + } 993 + 994 + return 0; 995 + } 996 + 997 + static int hx8279_probe(struct mipi_dsi_device *dsi) 998 + { 999 + struct device *dev = &dsi->dev; 1000 + struct device_node *dsi_r; 1001 + struct hx8279 *hx; 1002 + int i, ret; 1003 + 1004 + hx = devm_drm_panel_alloc(dev, struct hx8279, panel, 1005 + &hx8279_panel_funcs, DRM_MODE_CONNECTOR_DSI); 1006 + if (IS_ERR(hx)) 1007 + return PTR_ERR(hx); 1008 + 1009 + ret = hx8279_init_vregs(hx, dev); 1010 + if (ret) 1011 + return ret; 1012 + 1013 + hx->desc = device_get_match_data(dev); 1014 + if (!hx->desc) 1015 + return -ENODEV; 1016 + 1017 + /* 1018 + * In some DriverICs some or all fields may be OTP: perform a 1019 + * basic configuration check before writing to help avoiding 1020 + * irreparable mistakes. 1021 + * 1022 + * Please note that this is not perfect and will only check if 1023 + * the values may be plausible; values that are wrong for a 1024 + * specific display, but still plausible for DrIC config will 1025 + * be accepted. 1026 + */ 1027 + ret = hx8279_check_params(hx, dev); 1028 + if (ret) 1029 + return dev_err_probe(dev, ret, "Invalid DriverIC configuration\n"); 1030 + 1031 + /* The enable line may be always tied to VCCIO, so this is optional */ 1032 + hx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS); 1033 + if (IS_ERR(hx->enable_gpio)) 1034 + return dev_err_probe(dev, PTR_ERR(hx->enable_gpio), 1035 + "Failed to get enable GPIO\n"); 1036 + 1037 + hx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); 1038 + if (IS_ERR(hx->reset_gpio)) 1039 + return dev_err_probe(dev, PTR_ERR(hx->reset_gpio), 1040 + "Failed to get reset GPIO\n"); 1041 + 1042 + /* If the panel is connected on two DSIs then DSI0 left, DSI1 right */ 1043 + dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); 1044 + if (dsi_r) { 1045 + const struct mipi_dsi_device_info *info = &hx->desc->dsi_info; 1046 + struct mipi_dsi_host *dsi_r_host; 1047 + 1048 + dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r); 1049 + of_node_put(dsi_r); 1050 + if (!dsi_r_host) 1051 + return dev_err_probe(dev, -EPROBE_DEFER, 1052 + "Cannot get secondary DSI host\n"); 1053 + 1054 + hx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi_r_host, info); 1055 + if (IS_ERR(hx->dsi[1])) 1056 + return dev_err_probe(dev, PTR_ERR(hx->dsi[1]), 1057 + "Cannot get secondary DSI node\n"); 1058 + mipi_dsi_set_drvdata(hx->dsi[1], hx); 1059 + } 1060 + 1061 + hx->dsi[0] = dsi; 1062 + mipi_dsi_set_drvdata(dsi, hx); 1063 + 1064 + ret = drm_panel_of_backlight(&hx->panel); 1065 + if (ret) 1066 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 1067 + 1068 + drm_panel_add(&hx->panel); 1069 + 1070 + for (i = 0; i < 2; i++) { 1071 + if (!hx->dsi[i]) 1072 + continue; 1073 + 1074 + hx->dsi[i]->lanes = hx->desc->num_lanes; 1075 + hx->dsi[i]->format = MIPI_DSI_FMT_RGB888; 1076 + 1077 + hx->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS | 1078 + MIPI_DSI_MODE_LPM; 1079 + 1080 + if (hx->desc->mode_data[0].is_video_mode) 1081 + hx->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO | 1082 + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 1083 + 1084 + ret = devm_mipi_dsi_attach(dev, hx->dsi[i]); 1085 + if (ret < 0) { 1086 + drm_panel_remove(&hx->panel); 1087 + return dev_err_probe(dev, ret, 1088 + "Cannot attach to DSI%d host.\n", i); 1089 + } 1090 + } 1091 + 1092 + return 0; 1093 + } 1094 + 1095 + static void hx8279_remove(struct mipi_dsi_device *dsi) 1096 + { 1097 + struct hx8279 *hx = mipi_dsi_get_drvdata(dsi); 1098 + 1099 + drm_panel_remove(&hx->panel); 1100 + } 1101 + 1102 + static const struct hx8279_panel_mode aoly_sl101pm1794fog_v15_modes[] = { 1103 + { 1104 + .mode = { 1105 + .clock = 159420, 1106 + .hdisplay = 1200, 1107 + .hsync_start = 1200 + 80, 1108 + .hsync_end = 1200 + 80 + 60, 1109 + .htotal = 1200 + 80 + 60 + 24, 1110 + .vdisplay = 1920, 1111 + .vsync_start = 1920 + 10, 1112 + .vsync_end = 1920 + 10 + 14, 1113 + .vtotal = 1920 + 10 + 14 + 4, 1114 + .width_mm = 136, 1115 + .height_mm = 217, 1116 + .type = DRM_MODE_TYPE_DRIVER 1117 + }, 1118 + .bpc = 8, 1119 + .is_video_mode = true, 1120 + }, 1121 + }; 1122 + 1123 + static const struct hx8279_panel_mode startek_kd070fhfid078_modes[] = { 1124 + { 1125 + .mode = { 1126 + .clock = 156458, 1127 + .hdisplay = 1200, 1128 + .hsync_start = 1200 + 50, 1129 + .hsync_end = 1200 + 50 + 24, 1130 + .htotal = 1200 + 50 + 24 + 66, 1131 + .vdisplay = 1920, 1132 + .vsync_start = 1920 + 14, 1133 + .vsync_end = 1920 + 14 + 2, 1134 + .vtotal = 1920 + 14 + 2 + 10, 1135 + .width_mm = 95, 1136 + .height_mm = 151, 1137 + .type = DRM_MODE_TYPE_DRIVER 1138 + }, 1139 + .bpc = 8, 1140 + .is_video_mode = true, 1141 + }, 1142 + }; 1143 + 1144 + static const struct hx8279_goa_mux aoly_sl101pm1794fog_v15_gmux = { 1145 + .gout_l = { 0x5, 0x5, 0xb, 0xb, 0x9, 0x9, 0x16, 0x16, 0xe, 0xe, 1146 + 0x7, 0x7, 0x26, 0x26, 0x15, 0x15, 0x1, 0x1, 0x3, 0x3 }, 1147 + .gout_r = { 0x6, 0x6, 0xc, 0xc, 0xa, 0xa, 0x16, 0x16, 0xe, 0xe, 1148 + 0x8, 0x8, 0x26, 0x26, 0x15, 0x15, 0x2, 0x2, 0x4, 0x4 }, 1149 + }; 1150 + 1151 + static const struct hx8279_analog_gamma aoly_sl101pm1794fog_v15_ana_gamma = { 1152 + .pos = { 0x0, 0xd, 0x17, 0x26, 0x31, 0x1c, 0x2c, 0x33, 0x31, 1153 + 0x37, 0x37, 0x37, 0x39, 0x2e, 0x2f, 0x2f, 0x7 }, 1154 + .neg = { 0x0, 0xd, 0x17, 0x26, 0x31, 0x3f, 0x3f, 0x3f, 0x3f, 1155 + 0x37, 0x37, 0x37, 0x39, 0x2e, 0x2f, 0x2f, 0x7 }, 1156 + }; 1157 + 1158 + static const struct hx8279_digital_gamma aoly_sl101pm1794fog_v15_dig_gamma = { 1159 + .r = { 0x0, 0x5, 0x10, 0x22, 0x36, 0x4a, 0x6c, 0x9a, 0xd7, 0x17, 1160 + 0x92, 0x15, 0x18, 0x8c, 0x0, 0x3a, 0x72, 0x8c, 0xa5, 0xb1, 1161 + 0xbe, 0xca, 0xd1, 0xd4, 0x0, 0x0, 0x16, 0xaf, 0xff, 0xff }, 1162 + .g = { 0x4, 0x5, 0x11, 0x24, 0x39, 0x4e, 0x72, 0xa3, 0xe1, 0x25, 1163 + 0xa8, 0x2e, 0x32, 0xad, 0x28, 0x63, 0x9b, 0xb5, 0xcf, 0xdb, 1164 + 0xe8, 0xf5, 0xfa, 0xfc, 0x0, 0x0, 0x16, 0xaf, 0xff, 0xff }, 1165 + .b = { 0x4, 0x4, 0xf, 0x22, 0x37, 0x4d, 0x71, 0xa2, 0xe1, 0x26, 1166 + 0xa9, 0x2f, 0x33, 0xac, 0x24, 0x5d, 0x94, 0xac, 0xc5, 0xd1, 1167 + 0xdc, 0xe8, 0xed, 0xf0, 0x0, 0x0, 0x16, 0xaf, 0xff, 0xff }, 1168 + }; 1169 + 1170 + static const struct hx8279_panel_desc aoly_sl101pm1794fog_v15 = { 1171 + .dsi_info = { 1172 + .type = "L101PM1794FOG-V15", 1173 + .channel = 0, 1174 + .node = NULL, 1175 + }, 1176 + .mode_data = aoly_sl101pm1794fog_v15_modes, 1177 + .num_modes = ARRAY_SIZE(aoly_sl101pm1794fog_v15_modes), 1178 + .num_lanes = 4, 1179 + 1180 + /* Driver/Module Configuration: LC Matrix voltages */ 1181 + .vgh_mv = 16500, 1182 + .vgl_mv = 11200, 1183 + .vgph_mv = 4600, 1184 + .vgnh_mv = 4600, 1185 + 1186 + /* Analog Gamma correction */ 1187 + .agamma = &aoly_sl101pm1794fog_v15_ana_gamma, 1188 + 1189 + /* Gate driver On Array (GOA) Muxing */ 1190 + .gmux = &aoly_sl101pm1794fog_v15_gmux, 1191 + 1192 + /* Gate driver On Array (GOA) Mux Config */ 1193 + .goa_unk_ba = 0xf0, 1194 + .goa_odd_timing = { 0, 0, 0, 42, 0, 0 }, 1195 + .goa_even_timing = { 1, 42, 0, 0 }, 1196 + .goa_stv_lead_time_ck = 11, 1197 + .goa_ckv_lead_time_ck = 7, 1198 + .goa_ckv_dummy_vblank_num = 3, 1199 + .goa_ckv_rise_precharge = 1, 1200 + .goa_clr1_width_adj = 0, 1201 + .goa_clr234_width_adj = 0, 1202 + .goa_clr_polarity = { 1, 0, 0, 0 }, 1203 + .goa_clr_start_pos = { 8, 9, 3, 4 }, 1204 + .goa_unk_e4 = 0xc0, 1205 + .goa_unk_e5 = 0x0d, 1206 + 1207 + /* MIPI Configuration */ 1208 + .bta_tlpx = 2, 1209 + .lhs_settle_time_by_osc25 = true, 1210 + .ths_settle_time = 2, 1211 + .timing_unk_b8 = 0xa5, 1212 + .timing_unk_bc = 0x20, 1213 + .timing_unk_d6 = 0x7f, 1214 + 1215 + /* ENG/Gamma Configuration */ 1216 + .gamma_ctl = 0, 1217 + .volt_adj = FIELD_PREP_CONST(HX8279_P6_VOLT_ADJ_VCCIFS, 3) | 1218 + FIELD_PREP_CONST(HX8279_P6_VOLT_ADJ_VCCS, 3), 1219 + .src_delay_time_adj_ck = 50, 1220 + 1221 + /* Digital Gamma Adjustment */ 1222 + .dgamma = &aoly_sl101pm1794fog_v15_dig_gamma, 1223 + }; 1224 + 1225 + static const struct hx8279_goa_mux startek_kd070fhfid078_gmux = { 1226 + .gout_l = { 0xd, 0xd, 0x6, 0x6, 0x8, 0x8, 0xa, 0xa, 0xc, 0xc, 1227 + 0x0, 0x0, 0xe, 0xe, 0x1, 0x1, 0x4, 0x4, 0x0, 0x0 }, 1228 + .gout_r = { 0xd, 0xd, 0x5, 0x5, 0x7, 0x7, 0x9, 0x9, 0xb, 0xb, 1229 + 0x0, 0x0, 0xe, 0xe, 0x1, 0x1, 0x3, 0x3, 0x0, 0x0 }, 1230 + }; 1231 + 1232 + static const struct hx8279_panel_desc startek_kd070fhfid078 = { 1233 + .dsi_info = { 1234 + .type = "KD070FHFID078", 1235 + .channel = 0, 1236 + .node = NULL, 1237 + }, 1238 + .mode_data = startek_kd070fhfid078_modes, 1239 + .num_modes = ARRAY_SIZE(startek_kd070fhfid078_modes), 1240 + .num_lanes = 4, 1241 + 1242 + /* Driver/Module Configuration: LC Matrix voltages */ 1243 + .vgh_mv = 18000, 1244 + .vgl_mv = 12100, 1245 + .vgph_mv = 5500, 1246 + .vgnh_mv = 5500, 1247 + 1248 + /* Gate driver On Array (GOA) Mux Config */ 1249 + .gmux = &startek_kd070fhfid078_gmux, 1250 + 1251 + /* Gate driver On Array (GOA) Configuration */ 1252 + .goa_unk_ba = 0xf0, 1253 + .goa_stv_lead_time_ck = 7, 1254 + .goa_ckv_lead_time_ck = 3, 1255 + .goa_ckv_dummy_vblank_num = 1, 1256 + .goa_ckv_rise_precharge = 0, 1257 + .goa_ckv_fall_precharge = 0, 1258 + .goa_clr1_width_adj = 1, 1259 + .goa_clr234_width_adj = 5, 1260 + .goa_clr_polarity = { 0, 1, -1, -1 }, 1261 + .goa_clr_start_pos = { 5, 10, -1, -1 }, 1262 + .goa_unk_e4 = 0xc0, 1263 + .goa_unk_e5 = 0x00, 1264 + 1265 + /* MIPI Configuration */ 1266 + .bta_tlpx = 2, 1267 + .lhs_settle_time_by_osc25 = true, 1268 + .ths_settle_time = 2, 1269 + .timing_unk_b8 = 0x7f, 1270 + .timing_unk_bc = 0x20, 1271 + .timing_unk_d6 = 0x7f, 1272 + 1273 + /* ENG/Gamma Configuration */ 1274 + .gamma_ctl = FIELD_PREP_CONST(HX8279_P6_GAMMA_POCGM_CTL, 1) | 1275 + FIELD_PREP_CONST(HX8279_P6_GAMMA_POGCMD_CTL, 1), 1276 + .src_delay_time_adj_ck = 72, 1277 + }; 1278 + 1279 + static const struct of_device_id hx8279_of_match[] = { 1280 + { .compatible = "aoly,sl101pm1794fog-v15", .data = &aoly_sl101pm1794fog_v15 }, 1281 + { .compatible = "startek,kd070fhfid078", .data = &startek_kd070fhfid078 }, 1282 + { /* sentinel */ } 1283 + }; 1284 + MODULE_DEVICE_TABLE(of, hx8279_of_match); 1285 + 1286 + static struct mipi_dsi_driver hx8279_driver = { 1287 + .probe = hx8279_probe, 1288 + .remove = hx8279_remove, 1289 + .driver = { 1290 + .name = "panel-himax-hx8279", 1291 + .of_match_table = hx8279_of_match, 1292 + }, 1293 + }; 1294 + module_mipi_dsi_driver(hx8279_driver); 1295 + 1296 + MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); 1297 + MODULE_DESCRIPTION("Himax HX8279 DriverIC panels driver"); 1298 + MODULE_LICENSE("GPL");