1*88148c30SDavid Heidelberg // SPDX-License-Identifier: GPL-2.0-only 2*88148c30SDavid Heidelberg /* 3*88148c30SDavid Heidelberg * Copyright (c) 2022 Nia Espera <a5b6@riseup.net> 4*88148c30SDavid Heidelberg * Copyright (c) 2025 David Heidelberg <david@ixit.cz> 5*88148c30SDavid Heidelberg */ 6*88148c30SDavid Heidelberg 7*88148c30SDavid Heidelberg #include <linux/delay.h> 8*88148c30SDavid Heidelberg #include <linux/gpio/consumer.h> 9*88148c30SDavid Heidelberg #include <linux/module.h> 10*88148c30SDavid Heidelberg #include <linux/of.h> 11*88148c30SDavid Heidelberg #include <linux/of_device.h> 12*88148c30SDavid Heidelberg #include <linux/regulator/consumer.h> 13*88148c30SDavid Heidelberg #include <linux/swab.h> 14*88148c30SDavid Heidelberg #include <linux/backlight.h> 15*88148c30SDavid Heidelberg 16*88148c30SDavid Heidelberg #include <video/mipi_display.h> 17*88148c30SDavid Heidelberg 18*88148c30SDavid Heidelberg #include <drm/drm_mipi_dsi.h> 19*88148c30SDavid Heidelberg #include <drm/drm_modes.h> 20*88148c30SDavid Heidelberg #include <drm/drm_panel.h> 21*88148c30SDavid Heidelberg #include <drm/drm_probe_helper.h> 22*88148c30SDavid Heidelberg 23*88148c30SDavid Heidelberg #define MCS_ELVSS_ON 0xb1 24*88148c30SDavid Heidelberg 25*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 { 26*88148c30SDavid Heidelberg struct drm_panel panel; 27*88148c30SDavid Heidelberg struct mipi_dsi_device *dsi; 28*88148c30SDavid Heidelberg struct regulator_bulk_data *supplies; 29*88148c30SDavid Heidelberg struct gpio_desc *reset_gpio; 30*88148c30SDavid Heidelberg }; 31*88148c30SDavid Heidelberg 32*88148c30SDavid Heidelberg static const struct regulator_bulk_data s6e3fc2x01_supplies[] = { 33*88148c30SDavid Heidelberg { .supply = "vddio" }, 34*88148c30SDavid Heidelberg { .supply = "vci" }, 35*88148c30SDavid Heidelberg { .supply = "poc" }, 36*88148c30SDavid Heidelberg }; 37*88148c30SDavid Heidelberg 38*88148c30SDavid Heidelberg static inline 39*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *to_samsung_s6e3fc2x01(struct drm_panel *panel) 40*88148c30SDavid Heidelberg { 41*88148c30SDavid Heidelberg return container_of(panel, struct samsung_s6e3fc2x01, panel); 42*88148c30SDavid Heidelberg } 43*88148c30SDavid Heidelberg 44*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_on_lvl1(ctx) \ 45*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0xa5, 0xa5) 46*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_off_lvl1(ctx) \ 47*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0x5a, 0x5a) 48*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_on_lvl2(ctx) \ 49*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a) 50*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_off_lvl2(ctx) \ 51*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5) 52*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_on_lvl3(ctx) \ 53*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x5a, 0x5a) 54*88148c30SDavid Heidelberg #define s6e3fc2x01_test_key_off_lvl3(ctx) \ 55*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0xa5, 0xa5) 56*88148c30SDavid Heidelberg 57*88148c30SDavid Heidelberg static void s6e3fc2x01_reset(struct samsung_s6e3fc2x01 *ctx) 58*88148c30SDavid Heidelberg { 59*88148c30SDavid Heidelberg gpiod_set_value_cansleep(ctx->reset_gpio, 1); 60*88148c30SDavid Heidelberg usleep_range(5000, 6000); 61*88148c30SDavid Heidelberg gpiod_set_value_cansleep(ctx->reset_gpio, 0); 62*88148c30SDavid Heidelberg usleep_range(5000, 6000); 63*88148c30SDavid Heidelberg } 64*88148c30SDavid Heidelberg 65*88148c30SDavid Heidelberg static int s6e3fc2x01_on(struct samsung_s6e3fc2x01 *ctx) 66*88148c30SDavid Heidelberg { 67*88148c30SDavid Heidelberg struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 68*88148c30SDavid Heidelberg 69*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl1(&dsi_ctx); 70*88148c30SDavid Heidelberg 71*88148c30SDavid Heidelberg mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 72*88148c30SDavid Heidelberg 73*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 74*88148c30SDavid Heidelberg 75*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x0a); 76*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 77*88148c30SDavid Heidelberg 78*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl1(&dsi_ctx); 79*88148c30SDavid Heidelberg 80*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 81*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x01); 82*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x01); 83*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 84*88148c30SDavid Heidelberg 85*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 15000, 16000); 86*88148c30SDavid Heidelberg 87*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x0f); 88*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 89*88148c30SDavid Heidelberg 90*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl1(&dsi_ctx); 91*88148c30SDavid Heidelberg mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 92*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl1(&dsi_ctx); 93*88148c30SDavid Heidelberg 94*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 95*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xeb, 0x17, 96*88148c30SDavid Heidelberg 0x41, 0x92, 97*88148c30SDavid Heidelberg 0x0e, 0x10, 98*88148c30SDavid Heidelberg 0x82, 0x5a); 99*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 100*88148c30SDavid Heidelberg 101*88148c30SDavid Heidelberg /* Column & Page Address Setting */ 102*88148c30SDavid Heidelberg mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 0x0437); 103*88148c30SDavid Heidelberg mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 0x0923); 104*88148c30SDavid Heidelberg 105*88148c30SDavid Heidelberg /* Horizontal & Vertical sync Setting */ 106*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 107*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x09); 108*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe8, 0x10, 0x30); 109*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 110*88148c30SDavid Heidelberg 111*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl3(&dsi_ctx); 112*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x01); 113*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0x88); 114*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07); 115*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x67); 116*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl3(&dsi_ctx); 117*88148c30SDavid Heidelberg 118*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 119*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07); 120*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb7, 0x01); 121*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x08); 122*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb7, 0x12); 123*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 124*88148c30SDavid Heidelberg 125*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); 126*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 127*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); 128*88148c30SDavid Heidelberg 129*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 130*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0x00, 0x01); 131*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 132*88148c30SDavid Heidelberg 133*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 134*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x00, 0xc1); 135*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 136*88148c30SDavid Heidelberg 137*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x78); 138*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 139*88148c30SDavid Heidelberg 140*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x90); 141*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 142*88148c30SDavid Heidelberg 143*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 144*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02); 145*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0xc6, 0x00, 0x00, 146*88148c30SDavid Heidelberg 0x21, 0xed, 0x02, 0x08, 0x06, 0xc1, 0x27, 147*88148c30SDavid Heidelberg 0xfc, 0xdc, 0xe4, 0x00, 0xd9, 0xe6, 0xe7, 148*88148c30SDavid Heidelberg 0x00, 0xfc, 0xff, 0xea); 149*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0x00, 0x00); 150*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 151*88148c30SDavid Heidelberg 152*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 153*88148c30SDavid Heidelberg 154*88148c30SDavid Heidelberg return dsi_ctx.accum_err; 155*88148c30SDavid Heidelberg } 156*88148c30SDavid Heidelberg 157*88148c30SDavid Heidelberg static int s6e3fc2x01_enable(struct drm_panel *panel) 158*88148c30SDavid Heidelberg { 159*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel); 160*88148c30SDavid Heidelberg struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 161*88148c30SDavid Heidelberg 162*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl1(&dsi_ctx); 163*88148c30SDavid Heidelberg mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 164*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl1(&dsi_ctx); 165*88148c30SDavid Heidelberg 166*88148c30SDavid Heidelberg return dsi_ctx.accum_err; 167*88148c30SDavid Heidelberg } 168*88148c30SDavid Heidelberg 169*88148c30SDavid Heidelberg static int s6e3fc2x01_off(struct samsung_s6e3fc2x01 *ctx) 170*88148c30SDavid Heidelberg { 171*88148c30SDavid Heidelberg struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 172*88148c30SDavid Heidelberg 173*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl1(&dsi_ctx); 174*88148c30SDavid Heidelberg 175*88148c30SDavid Heidelberg mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 176*88148c30SDavid Heidelberg 177*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); 178*88148c30SDavid Heidelberg 179*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 180*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000); 181*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x50); 182*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x82); 183*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 184*88148c30SDavid Heidelberg mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000); 185*88148c30SDavid Heidelberg 186*88148c30SDavid Heidelberg mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 187*88148c30SDavid Heidelberg 188*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl1(&dsi_ctx); 189*88148c30SDavid Heidelberg 190*88148c30SDavid Heidelberg s6e3fc2x01_test_key_on_lvl2(&dsi_ctx); 191*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x05); 192*88148c30SDavid Heidelberg mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x01); 193*88148c30SDavid Heidelberg s6e3fc2x01_test_key_off_lvl2(&dsi_ctx); 194*88148c30SDavid Heidelberg mipi_dsi_msleep(&dsi_ctx, 160); 195*88148c30SDavid Heidelberg 196*88148c30SDavid Heidelberg return dsi_ctx.accum_err; 197*88148c30SDavid Heidelberg } 198*88148c30SDavid Heidelberg 199*88148c30SDavid Heidelberg static int s6e3fc2x01_disable(struct drm_panel *panel) 200*88148c30SDavid Heidelberg { 201*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel); 202*88148c30SDavid Heidelberg 203*88148c30SDavid Heidelberg s6e3fc2x01_off(ctx); 204*88148c30SDavid Heidelberg 205*88148c30SDavid Heidelberg return 0; 206*88148c30SDavid Heidelberg } 207*88148c30SDavid Heidelberg 208*88148c30SDavid Heidelberg static int s6e3fc2x01_prepare(struct drm_panel *panel) 209*88148c30SDavid Heidelberg { 210*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel); 211*88148c30SDavid Heidelberg int ret; 212*88148c30SDavid Heidelberg 213*88148c30SDavid Heidelberg ret = regulator_bulk_enable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies); 214*88148c30SDavid Heidelberg if (ret < 0) 215*88148c30SDavid Heidelberg return ret; 216*88148c30SDavid Heidelberg 217*88148c30SDavid Heidelberg s6e3fc2x01_reset(ctx); 218*88148c30SDavid Heidelberg 219*88148c30SDavid Heidelberg ret = s6e3fc2x01_on(ctx); 220*88148c30SDavid Heidelberg if (ret < 0) { 221*88148c30SDavid Heidelberg gpiod_set_value_cansleep(ctx->reset_gpio, 1); 222*88148c30SDavid Heidelberg regulator_bulk_disable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies); 223*88148c30SDavid Heidelberg return ret; 224*88148c30SDavid Heidelberg } 225*88148c30SDavid Heidelberg 226*88148c30SDavid Heidelberg return 0; 227*88148c30SDavid Heidelberg } 228*88148c30SDavid Heidelberg 229*88148c30SDavid Heidelberg static int s6e3fc2x01_unprepare(struct drm_panel *panel) 230*88148c30SDavid Heidelberg { 231*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel); 232*88148c30SDavid Heidelberg 233*88148c30SDavid Heidelberg gpiod_set_value_cansleep(ctx->reset_gpio, 1); 234*88148c30SDavid Heidelberg regulator_bulk_disable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies); 235*88148c30SDavid Heidelberg 236*88148c30SDavid Heidelberg return 0; 237*88148c30SDavid Heidelberg } 238*88148c30SDavid Heidelberg 239*88148c30SDavid Heidelberg static const struct drm_display_mode ams641rw_mode = { 240*88148c30SDavid Heidelberg .clock = (1080 + 72 + 16 + 36) * (2340 + 32 + 4 + 18) * 60 / 1000, 241*88148c30SDavid Heidelberg .hdisplay = 1080, 242*88148c30SDavid Heidelberg .hsync_start = 1080 + 72, 243*88148c30SDavid Heidelberg .hsync_end = 1080 + 72 + 16, 244*88148c30SDavid Heidelberg .htotal = 1080 + 72 + 16 + 36, 245*88148c30SDavid Heidelberg .vdisplay = 2340, 246*88148c30SDavid Heidelberg .vsync_start = 2340 + 32, 247*88148c30SDavid Heidelberg .vsync_end = 2340 + 32 + 4, 248*88148c30SDavid Heidelberg .vtotal = 2340 + 32 + 4 + 18, 249*88148c30SDavid Heidelberg .width_mm = 68, 250*88148c30SDavid Heidelberg .height_mm = 145, 251*88148c30SDavid Heidelberg }; 252*88148c30SDavid Heidelberg 253*88148c30SDavid Heidelberg static int s6e3fc2x01_get_modes(struct drm_panel *panel, 254*88148c30SDavid Heidelberg struct drm_connector *connector) 255*88148c30SDavid Heidelberg { 256*88148c30SDavid Heidelberg return drm_connector_helper_get_modes_fixed(connector, &ams641rw_mode); 257*88148c30SDavid Heidelberg } 258*88148c30SDavid Heidelberg 259*88148c30SDavid Heidelberg static const struct drm_panel_funcs samsung_s6e3fc2x01_panel_funcs = { 260*88148c30SDavid Heidelberg .prepare = s6e3fc2x01_prepare, 261*88148c30SDavid Heidelberg .enable = s6e3fc2x01_enable, 262*88148c30SDavid Heidelberg .disable = s6e3fc2x01_disable, 263*88148c30SDavid Heidelberg .unprepare = s6e3fc2x01_unprepare, 264*88148c30SDavid Heidelberg .get_modes = s6e3fc2x01_get_modes, 265*88148c30SDavid Heidelberg }; 266*88148c30SDavid Heidelberg 267*88148c30SDavid Heidelberg static int s6e3fc2x01_panel_bl_update_status(struct backlight_device *bl) 268*88148c30SDavid Heidelberg { 269*88148c30SDavid Heidelberg struct mipi_dsi_device *dsi = bl_get_data(bl); 270*88148c30SDavid Heidelberg u16 brightness = backlight_get_brightness(bl); 271*88148c30SDavid Heidelberg int err; 272*88148c30SDavid Heidelberg 273*88148c30SDavid Heidelberg dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 274*88148c30SDavid Heidelberg 275*88148c30SDavid Heidelberg err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 276*88148c30SDavid Heidelberg if (err < 0) 277*88148c30SDavid Heidelberg return err; 278*88148c30SDavid Heidelberg 279*88148c30SDavid Heidelberg dsi->mode_flags |= MIPI_DSI_MODE_LPM; 280*88148c30SDavid Heidelberg 281*88148c30SDavid Heidelberg return 0; 282*88148c30SDavid Heidelberg } 283*88148c30SDavid Heidelberg 284*88148c30SDavid Heidelberg static const struct backlight_ops s6e3fc2x01_panel_bl_ops = { 285*88148c30SDavid Heidelberg .update_status = s6e3fc2x01_panel_bl_update_status, 286*88148c30SDavid Heidelberg }; 287*88148c30SDavid Heidelberg 288*88148c30SDavid Heidelberg static struct backlight_device * 289*88148c30SDavid Heidelberg s6e3fc2x01_create_backlight(struct mipi_dsi_device *dsi) 290*88148c30SDavid Heidelberg { 291*88148c30SDavid Heidelberg struct device *dev = &dsi->dev; 292*88148c30SDavid Heidelberg const struct backlight_properties props = { 293*88148c30SDavid Heidelberg .type = BACKLIGHT_PLATFORM, 294*88148c30SDavid Heidelberg .brightness = 512, 295*88148c30SDavid Heidelberg .max_brightness = 1023, 296*88148c30SDavid Heidelberg }; 297*88148c30SDavid Heidelberg 298*88148c30SDavid Heidelberg return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 299*88148c30SDavid Heidelberg &s6e3fc2x01_panel_bl_ops, &props); 300*88148c30SDavid Heidelberg } 301*88148c30SDavid Heidelberg 302*88148c30SDavid Heidelberg static int s6e3fc2x01_probe(struct mipi_dsi_device *dsi) 303*88148c30SDavid Heidelberg { 304*88148c30SDavid Heidelberg struct device *dev = &dsi->dev; 305*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx; 306*88148c30SDavid Heidelberg int ret; 307*88148c30SDavid Heidelberg 308*88148c30SDavid Heidelberg ctx = devm_drm_panel_alloc(dev, struct samsung_s6e3fc2x01, panel, 309*88148c30SDavid Heidelberg &samsung_s6e3fc2x01_panel_funcs, 310*88148c30SDavid Heidelberg DRM_MODE_CONNECTOR_DSI); 311*88148c30SDavid Heidelberg if (IS_ERR(ctx)) 312*88148c30SDavid Heidelberg return PTR_ERR(ctx); 313*88148c30SDavid Heidelberg 314*88148c30SDavid Heidelberg ret = devm_regulator_bulk_get_const(dev, 315*88148c30SDavid Heidelberg ARRAY_SIZE(s6e3fc2x01_supplies), 316*88148c30SDavid Heidelberg s6e3fc2x01_supplies, 317*88148c30SDavid Heidelberg &ctx->supplies); 318*88148c30SDavid Heidelberg if (ret) 319*88148c30SDavid Heidelberg return dev_err_probe(dev, ret, "Failed to get regulators\n"); 320*88148c30SDavid Heidelberg 321*88148c30SDavid Heidelberg 322*88148c30SDavid Heidelberg /* keep the display on for flicker-free experience */ 323*88148c30SDavid Heidelberg ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 324*88148c30SDavid Heidelberg if (IS_ERR(ctx->reset_gpio)) 325*88148c30SDavid Heidelberg return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 326*88148c30SDavid Heidelberg "Failed to get reset-gpios\n"); 327*88148c30SDavid Heidelberg 328*88148c30SDavid Heidelberg ctx->dsi = dsi; 329*88148c30SDavid Heidelberg mipi_dsi_set_drvdata(dsi, ctx); 330*88148c30SDavid Heidelberg 331*88148c30SDavid Heidelberg dsi->lanes = 4; 332*88148c30SDavid Heidelberg dsi->format = MIPI_DSI_FMT_RGB888; 333*88148c30SDavid Heidelberg dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 334*88148c30SDavid Heidelberg MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 335*88148c30SDavid Heidelberg 336*88148c30SDavid Heidelberg ctx->panel.prepare_prev_first = true; 337*88148c30SDavid Heidelberg 338*88148c30SDavid Heidelberg ctx->panel.backlight = s6e3fc2x01_create_backlight(dsi); 339*88148c30SDavid Heidelberg if (IS_ERR(ctx->panel.backlight)) 340*88148c30SDavid Heidelberg return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 341*88148c30SDavid Heidelberg "Failed to create backlight\n"); 342*88148c30SDavid Heidelberg 343*88148c30SDavid Heidelberg drm_panel_add(&ctx->panel); 344*88148c30SDavid Heidelberg 345*88148c30SDavid Heidelberg ret = mipi_dsi_attach(dsi); 346*88148c30SDavid Heidelberg if (ret < 0) { 347*88148c30SDavid Heidelberg dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 348*88148c30SDavid Heidelberg drm_panel_remove(&ctx->panel); 349*88148c30SDavid Heidelberg return ret; 350*88148c30SDavid Heidelberg } 351*88148c30SDavid Heidelberg 352*88148c30SDavid Heidelberg return 0; 353*88148c30SDavid Heidelberg } 354*88148c30SDavid Heidelberg 355*88148c30SDavid Heidelberg static void s6e3fc2x01_remove(struct mipi_dsi_device *dsi) 356*88148c30SDavid Heidelberg { 357*88148c30SDavid Heidelberg struct samsung_s6e3fc2x01 *ctx = mipi_dsi_get_drvdata(dsi); 358*88148c30SDavid Heidelberg int ret; 359*88148c30SDavid Heidelberg 360*88148c30SDavid Heidelberg ret = mipi_dsi_detach(dsi); 361*88148c30SDavid Heidelberg if (ret < 0) 362*88148c30SDavid Heidelberg dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 363*88148c30SDavid Heidelberg 364*88148c30SDavid Heidelberg drm_panel_remove(&ctx->panel); 365*88148c30SDavid Heidelberg } 366*88148c30SDavid Heidelberg 367*88148c30SDavid Heidelberg static const struct of_device_id s6e3fc2x01_of_match[] = { 368*88148c30SDavid Heidelberg { .compatible = "samsung,s6e3fc2x01-ams641rw", .data = &ams641rw_mode }, 369*88148c30SDavid Heidelberg { /* sentinel */ } 370*88148c30SDavid Heidelberg }; 371*88148c30SDavid Heidelberg MODULE_DEVICE_TABLE(of, s6e3fc2x01_of_match); 372*88148c30SDavid Heidelberg 373*88148c30SDavid Heidelberg static struct mipi_dsi_driver s6e3fc2x01_driver = { 374*88148c30SDavid Heidelberg .probe = s6e3fc2x01_probe, 375*88148c30SDavid Heidelberg .remove = s6e3fc2x01_remove, 376*88148c30SDavid Heidelberg .driver = { 377*88148c30SDavid Heidelberg .name = "panel-samsung-s6e3fc2x01", 378*88148c30SDavid Heidelberg .of_match_table = s6e3fc2x01_of_match, 379*88148c30SDavid Heidelberg }, 380*88148c30SDavid Heidelberg }; 381*88148c30SDavid Heidelberg module_mipi_dsi_driver(s6e3fc2x01_driver); 382*88148c30SDavid Heidelberg 383*88148c30SDavid Heidelberg MODULE_AUTHOR("David Heidelberg <david@ixit.cz>"); 384*88148c30SDavid Heidelberg MODULE_DESCRIPTION("DRM driver for Samsung S6E3FC2X01 DDIC"); 385*88148c30SDavid Heidelberg MODULE_LICENSE("GPL"); 386