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