1*dd7ec83fSArpit Saini // SPDX-License-Identifier: GPL-2.0-only 2*dd7ec83fSArpit Saini /* 3*dd7ec83fSArpit Saini * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4*dd7ec83fSArpit Saini */ 5*dd7ec83fSArpit Saini 6*dd7ec83fSArpit Saini #include <linux/backlight.h> 7*dd7ec83fSArpit Saini #include <linux/delay.h> 8*dd7ec83fSArpit Saini #include <linux/gpio/consumer.h> 9*dd7ec83fSArpit Saini #include <linux/module.h> 10*dd7ec83fSArpit Saini #include <linux/of.h> 11*dd7ec83fSArpit Saini #include <linux/regulator/consumer.h> 12*dd7ec83fSArpit Saini 13*dd7ec83fSArpit Saini #include <video/mipi_display.h> 14*dd7ec83fSArpit Saini 15*dd7ec83fSArpit Saini #include <drm/drm_mipi_dsi.h> 16*dd7ec83fSArpit Saini #include <drm/drm_modes.h> 17*dd7ec83fSArpit Saini #include <drm/drm_panel.h> 18*dd7ec83fSArpit Saini #include <drm/drm_probe_helper.h> 19*dd7ec83fSArpit Saini 20*dd7ec83fSArpit Saini struct panel_desc { 21*dd7ec83fSArpit Saini const struct drm_display_mode *mode; 22*dd7ec83fSArpit Saini unsigned int lanes; 23*dd7ec83fSArpit Saini enum mipi_dsi_pixel_format format; 24*dd7ec83fSArpit Saini unsigned long mode_flags; 25*dd7ec83fSArpit Saini void (*init)(struct mipi_dsi_multi_context *dsi_ctx); 26*dd7ec83fSArpit Saini }; 27*dd7ec83fSArpit Saini 28*dd7ec83fSArpit Saini struct ili7807s { 29*dd7ec83fSArpit Saini struct drm_panel panel; 30*dd7ec83fSArpit Saini struct mipi_dsi_device *dsi; 31*dd7ec83fSArpit Saini const struct panel_desc *desc; 32*dd7ec83fSArpit Saini 33*dd7ec83fSArpit Saini struct regulator_bulk_data *supplies; 34*dd7ec83fSArpit Saini struct gpio_desc *reset_gpio; 35*dd7ec83fSArpit Saini }; 36*dd7ec83fSArpit Saini 37*dd7ec83fSArpit Saini static const struct regulator_bulk_data ili7807s_supplies[] = { 38*dd7ec83fSArpit Saini { .supply = "vddi" }, 39*dd7ec83fSArpit Saini { .supply = "avdd" }, 40*dd7ec83fSArpit Saini { .supply = "avee" }, 41*dd7ec83fSArpit Saini }; 42*dd7ec83fSArpit Saini 43*dd7ec83fSArpit Saini static inline struct ili7807s *to_ili7807s(struct drm_panel *panel) 44*dd7ec83fSArpit Saini { 45*dd7ec83fSArpit Saini return container_of(panel, struct ili7807s, panel); 46*dd7ec83fSArpit Saini } 47*dd7ec83fSArpit Saini 48*dd7ec83fSArpit Saini static void ili7807s_reset(struct ili7807s *ctx) 49*dd7ec83fSArpit Saini { 50*dd7ec83fSArpit Saini gpiod_set_value_cansleep(ctx->reset_gpio, 1); 51*dd7ec83fSArpit Saini usleep_range(10000, 11000); 52*dd7ec83fSArpit Saini gpiod_set_value_cansleep(ctx->reset_gpio, 0); 53*dd7ec83fSArpit Saini usleep_range(10000, 11000); 54*dd7ec83fSArpit Saini } 55*dd7ec83fSArpit Saini 56*dd7ec83fSArpit Saini static void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 57*dd7ec83fSArpit Saini { 58*dd7ec83fSArpit Saini mipi_dsi_dcs_soft_reset_multi(dsi_ctx); 59*dd7ec83fSArpit Saini mipi_dsi_msleep(dsi_ctx, 120); 60*dd7ec83fSArpit Saini 61*dd7ec83fSArpit Saini mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00); 62*dd7ec83fSArpit Saini mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 63*dd7ec83fSArpit Saini mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99); 64*dd7ec83fSArpit Saini mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); 65*dd7ec83fSArpit Saini mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01); 66*dd7ec83fSArpit Saini mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff); 67*dd7ec83fSArpit Saini 68*dd7ec83fSArpit Saini mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx); 69*dd7ec83fSArpit Saini mipi_dsi_msleep(dsi_ctx, 120); 70*dd7ec83fSArpit Saini 71*dd7ec83fSArpit Saini mipi_dsi_dcs_set_display_on_multi(dsi_ctx); 72*dd7ec83fSArpit Saini mipi_dsi_msleep(dsi_ctx, 20); 73*dd7ec83fSArpit Saini } 74*dd7ec83fSArpit Saini 75*dd7ec83fSArpit Saini static int ili7807s_on(struct ili7807s *ctx) 76*dd7ec83fSArpit Saini { 77*dd7ec83fSArpit Saini struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 78*dd7ec83fSArpit Saini 79*dd7ec83fSArpit Saini ctx->desc->init(&dsi_ctx); 80*dd7ec83fSArpit Saini 81*dd7ec83fSArpit Saini return dsi_ctx.accum_err; 82*dd7ec83fSArpit Saini } 83*dd7ec83fSArpit Saini 84*dd7ec83fSArpit Saini static int ili7807s_off(struct ili7807s *ctx) 85*dd7ec83fSArpit Saini { 86*dd7ec83fSArpit Saini struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 87*dd7ec83fSArpit Saini 88*dd7ec83fSArpit Saini mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 89*dd7ec83fSArpit Saini mipi_dsi_msleep(&dsi_ctx, 20); 90*dd7ec83fSArpit Saini 91*dd7ec83fSArpit Saini mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 92*dd7ec83fSArpit Saini mipi_dsi_msleep(&dsi_ctx, 120); 93*dd7ec83fSArpit Saini 94*dd7ec83fSArpit Saini return dsi_ctx.accum_err; 95*dd7ec83fSArpit Saini } 96*dd7ec83fSArpit Saini 97*dd7ec83fSArpit Saini static int ili7807s_prepare(struct drm_panel *panel) 98*dd7ec83fSArpit Saini { 99*dd7ec83fSArpit Saini struct ili7807s *ctx = to_ili7807s(panel); 100*dd7ec83fSArpit Saini int ret; 101*dd7ec83fSArpit Saini 102*dd7ec83fSArpit Saini ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 103*dd7ec83fSArpit Saini if (ret < 0) { 104*dd7ec83fSArpit Saini dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret); 105*dd7ec83fSArpit Saini return ret; 106*dd7ec83fSArpit Saini } 107*dd7ec83fSArpit Saini 108*dd7ec83fSArpit Saini msleep(20); 109*dd7ec83fSArpit Saini 110*dd7ec83fSArpit Saini ili7807s_reset(ctx); 111*dd7ec83fSArpit Saini 112*dd7ec83fSArpit Saini ret = ili7807s_on(ctx); 113*dd7ec83fSArpit Saini if (ret < 0) { 114*dd7ec83fSArpit Saini dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret); 115*dd7ec83fSArpit Saini goto err; 116*dd7ec83fSArpit Saini } 117*dd7ec83fSArpit Saini 118*dd7ec83fSArpit Saini return 0; 119*dd7ec83fSArpit Saini 120*dd7ec83fSArpit Saini err: 121*dd7ec83fSArpit Saini gpiod_set_value_cansleep(ctx->reset_gpio, 1); 122*dd7ec83fSArpit Saini 123*dd7ec83fSArpit Saini regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 124*dd7ec83fSArpit Saini return ret; 125*dd7ec83fSArpit Saini } 126*dd7ec83fSArpit Saini 127*dd7ec83fSArpit Saini static int ili7807s_unprepare(struct drm_panel *panel) 128*dd7ec83fSArpit Saini { 129*dd7ec83fSArpit Saini struct ili7807s *ctx = to_ili7807s(panel); 130*dd7ec83fSArpit Saini int ret; 131*dd7ec83fSArpit Saini 132*dd7ec83fSArpit Saini ret = ili7807s_off(ctx); 133*dd7ec83fSArpit Saini if (ret < 0) 134*dd7ec83fSArpit Saini dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret); 135*dd7ec83fSArpit Saini 136*dd7ec83fSArpit Saini gpiod_set_value_cansleep(ctx->reset_gpio, 1); 137*dd7ec83fSArpit Saini 138*dd7ec83fSArpit Saini regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); 139*dd7ec83fSArpit Saini 140*dd7ec83fSArpit Saini return 0; 141*dd7ec83fSArpit Saini } 142*dd7ec83fSArpit Saini 143*dd7ec83fSArpit Saini static int ili7807s_get_modes(struct drm_panel *panel, 144*dd7ec83fSArpit Saini struct drm_connector *connector) 145*dd7ec83fSArpit Saini { 146*dd7ec83fSArpit Saini struct ili7807s *ctx = to_ili7807s(panel); 147*dd7ec83fSArpit Saini 148*dd7ec83fSArpit Saini return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode); 149*dd7ec83fSArpit Saini } 150*dd7ec83fSArpit Saini 151*dd7ec83fSArpit Saini static const struct drm_panel_funcs ili7807s_panel_funcs = { 152*dd7ec83fSArpit Saini .prepare = ili7807s_prepare, 153*dd7ec83fSArpit Saini .unprepare = ili7807s_unprepare, 154*dd7ec83fSArpit Saini .get_modes = ili7807s_get_modes, 155*dd7ec83fSArpit Saini }; 156*dd7ec83fSArpit Saini 157*dd7ec83fSArpit Saini static int ili7807s_bl_update_status(struct backlight_device *bl) 158*dd7ec83fSArpit Saini { 159*dd7ec83fSArpit Saini struct mipi_dsi_device *dsi = bl_get_data(bl); 160*dd7ec83fSArpit Saini u16 brightness = backlight_get_brightness(bl); 161*dd7ec83fSArpit Saini int ret; 162*dd7ec83fSArpit Saini 163*dd7ec83fSArpit Saini dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 164*dd7ec83fSArpit Saini 165*dd7ec83fSArpit Saini ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 166*dd7ec83fSArpit Saini 167*dd7ec83fSArpit Saini dsi->mode_flags |= MIPI_DSI_MODE_LPM; 168*dd7ec83fSArpit Saini 169*dd7ec83fSArpit Saini return ret; 170*dd7ec83fSArpit Saini } 171*dd7ec83fSArpit Saini 172*dd7ec83fSArpit Saini static const struct backlight_ops ili7807s_bl_ops = { 173*dd7ec83fSArpit Saini .update_status = ili7807s_bl_update_status, 174*dd7ec83fSArpit Saini }; 175*dd7ec83fSArpit Saini 176*dd7ec83fSArpit Saini static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi) 177*dd7ec83fSArpit Saini { 178*dd7ec83fSArpit Saini struct device *dev = &dsi->dev; 179*dd7ec83fSArpit Saini const struct backlight_properties props = { 180*dd7ec83fSArpit Saini .type = BACKLIGHT_RAW, 181*dd7ec83fSArpit Saini .brightness = 0x3fff, 182*dd7ec83fSArpit Saini .max_brightness = 0x3fff, 183*dd7ec83fSArpit Saini }; 184*dd7ec83fSArpit Saini 185*dd7ec83fSArpit Saini return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 186*dd7ec83fSArpit Saini &ili7807s_bl_ops, &props); 187*dd7ec83fSArpit Saini } 188*dd7ec83fSArpit Saini 189*dd7ec83fSArpit Saini static const struct drm_display_mode dlc0697_mode = { 190*dd7ec83fSArpit Saini .clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000, 191*dd7ec83fSArpit Saini 192*dd7ec83fSArpit Saini .hdisplay = 1080, 193*dd7ec83fSArpit Saini .hsync_start = 1080 + 18, 194*dd7ec83fSArpit Saini .hsync_end = 1080 + 18 + 2, 195*dd7ec83fSArpit Saini .htotal = 1080 + 18 + 2 + 16, 196*dd7ec83fSArpit Saini 197*dd7ec83fSArpit Saini .vdisplay = 1920, 198*dd7ec83fSArpit Saini .vsync_start = 1920 + 26, 199*dd7ec83fSArpit Saini .vsync_end = 1920 + 26 + 4, 200*dd7ec83fSArpit Saini .vtotal = 1920 + 26 + 4 + 20, 201*dd7ec83fSArpit Saini 202*dd7ec83fSArpit Saini .width_mm = 87, 203*dd7ec83fSArpit Saini .height_mm = 154, 204*dd7ec83fSArpit Saini .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 205*dd7ec83fSArpit Saini }; 206*dd7ec83fSArpit Saini 207*dd7ec83fSArpit Saini static const struct panel_desc dlc0697_desc = { 208*dd7ec83fSArpit Saini .mode = &dlc0697_mode, 209*dd7ec83fSArpit Saini .lanes = 4, 210*dd7ec83fSArpit Saini .format = MIPI_DSI_FMT_RGB888, 211*dd7ec83fSArpit Saini .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST, 212*dd7ec83fSArpit Saini .init = dlc0697_init_sequence, 213*dd7ec83fSArpit Saini }; 214*dd7ec83fSArpit Saini 215*dd7ec83fSArpit Saini static int ili7807s_probe(struct mipi_dsi_device *dsi) 216*dd7ec83fSArpit Saini { 217*dd7ec83fSArpit Saini struct device *dev = &dsi->dev; 218*dd7ec83fSArpit Saini const struct panel_desc *desc; 219*dd7ec83fSArpit Saini struct ili7807s *ctx; 220*dd7ec83fSArpit Saini int ret; 221*dd7ec83fSArpit Saini 222*dd7ec83fSArpit Saini ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel, 223*dd7ec83fSArpit Saini &ili7807s_panel_funcs, 224*dd7ec83fSArpit Saini DRM_MODE_CONNECTOR_DSI); 225*dd7ec83fSArpit Saini if (IS_ERR(ctx)) 226*dd7ec83fSArpit Saini return PTR_ERR(ctx); 227*dd7ec83fSArpit Saini 228*dd7ec83fSArpit Saini desc = of_device_get_match_data(dev); 229*dd7ec83fSArpit Saini ctx->desc = desc; 230*dd7ec83fSArpit Saini 231*dd7ec83fSArpit Saini ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies), 232*dd7ec83fSArpit Saini ili7807s_supplies, &ctx->supplies); 233*dd7ec83fSArpit Saini if (ret < 0) 234*dd7ec83fSArpit Saini return dev_err_probe(dev, ret, "failed to get regulators\n"); 235*dd7ec83fSArpit Saini 236*dd7ec83fSArpit Saini ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 237*dd7ec83fSArpit Saini if (IS_ERR(ctx->reset_gpio)) 238*dd7ec83fSArpit Saini return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 239*dd7ec83fSArpit Saini "failed to get reset gpio\n"); 240*dd7ec83fSArpit Saini 241*dd7ec83fSArpit Saini ctx->dsi = dsi; 242*dd7ec83fSArpit Saini mipi_dsi_set_drvdata(dsi, ctx); 243*dd7ec83fSArpit Saini 244*dd7ec83fSArpit Saini dsi->lanes = desc->lanes; 245*dd7ec83fSArpit Saini dsi->format = desc->format; 246*dd7ec83fSArpit Saini dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM; 247*dd7ec83fSArpit Saini 248*dd7ec83fSArpit Saini ctx->panel.prepare_prev_first = true; 249*dd7ec83fSArpit Saini 250*dd7ec83fSArpit Saini ret = drm_panel_of_backlight(&ctx->panel); 251*dd7ec83fSArpit Saini if (ret) 252*dd7ec83fSArpit Saini return ret; 253*dd7ec83fSArpit Saini 254*dd7ec83fSArpit Saini if (!ctx->panel.backlight) { 255*dd7ec83fSArpit Saini ctx->panel.backlight = ili7807s_create_backlight(dsi); 256*dd7ec83fSArpit Saini if (IS_ERR(ctx->panel.backlight)) 257*dd7ec83fSArpit Saini return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 258*dd7ec83fSArpit Saini "failed to create backlight\n"); 259*dd7ec83fSArpit Saini } 260*dd7ec83fSArpit Saini 261*dd7ec83fSArpit Saini ret = devm_drm_panel_add(dev, &ctx->panel); 262*dd7ec83fSArpit Saini if (ret < 0) 263*dd7ec83fSArpit Saini return dev_err_probe(dev, ret, "failed to add panel\n"); 264*dd7ec83fSArpit Saini 265*dd7ec83fSArpit Saini return devm_mipi_dsi_attach(dev, dsi); 266*dd7ec83fSArpit Saini } 267*dd7ec83fSArpit Saini 268*dd7ec83fSArpit Saini static const struct of_device_id ili7807s_of_match[] = { 269*dd7ec83fSArpit Saini { .compatible = "dlc,dlc0697", .data = &dlc0697_desc }, 270*dd7ec83fSArpit Saini { } 271*dd7ec83fSArpit Saini }; 272*dd7ec83fSArpit Saini MODULE_DEVICE_TABLE(of, ili7807s_of_match); 273*dd7ec83fSArpit Saini 274*dd7ec83fSArpit Saini static struct mipi_dsi_driver ili7807s_dsi_driver = { 275*dd7ec83fSArpit Saini .probe = ili7807s_probe, 276*dd7ec83fSArpit Saini .driver = { 277*dd7ec83fSArpit Saini .name = "panel-ilitek-ili7807s", 278*dd7ec83fSArpit Saini .of_match_table = ili7807s_of_match, 279*dd7ec83fSArpit Saini }, 280*dd7ec83fSArpit Saini }; 281*dd7ec83fSArpit Saini module_mipi_dsi_driver(ili7807s_dsi_driver); 282*dd7ec83fSArpit Saini 283*dd7ec83fSArpit Saini MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>"); 284*dd7ec83fSArpit Saini MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel"); 285*dd7ec83fSArpit Saini MODULE_LICENSE("GPL"); 286