1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/array_size.h> 4 #include <linux/delay.h> 5 #include <linux/err.h> 6 #include <linux/gpio/consumer.h> 7 #include <linux/mod_devicetable.h> 8 #include <linux/module.h> 9 #include <linux/property.h> 10 #include <linux/regulator/consumer.h> 11 12 #include <video/mipi_display.h> 13 14 #include <drm/drm_mipi_dsi.h> 15 #include <drm/drm_modes.h> 16 #include <drm/drm_panel.h> 17 #include <drm/drm_probe_helper.h> 18 19 static const struct regulator_bulk_data mot_panel_supplies[] = { 20 { .supply = "vddio" }, { .supply = "vdd" }, 21 }; 22 23 struct mot_panel { 24 struct drm_panel panel; 25 struct mipi_dsi_device *dsi; 26 27 struct gpio_desc *reset_gpio; 28 29 struct regulator_bulk_data *supplies; 30 }; 31 32 static inline struct mot_panel *to_mot_panel(struct drm_panel *panel) 33 { 34 return container_of(panel, struct mot_panel, panel); 35 } 36 37 static void mot_panel_reset(struct mot_panel *priv) 38 { 39 gpiod_set_value_cansleep(priv->reset_gpio, 1); 40 usleep_range(50000, 51000); 41 gpiod_set_value_cansleep(priv->reset_gpio, 0); 42 usleep_range(10000, 11000); 43 } 44 45 static void mot_es2(struct mipi_dsi_multi_context *ctx) 46 { 47 mipi_dsi_generic_write_seq_multi(ctx, 0x55, 0x01); 48 49 mipi_dsi_dcs_exit_sleep_mode_multi(ctx); 50 mipi_dsi_msleep(ctx, 120); 51 52 mipi_dsi_generic_write_seq_multi(ctx, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49, 53 0x74, 0x29, 0x12, 0x15, 0x2f, 0x2f, 0x04); 54 mipi_dsi_generic_write_seq_multi(ctx, 0xf8, 0x4b, 0x04, 0x10, 0x1a, 0x2c, 0x2c, 55 0x2c, 0x2c, 0x14, 0x12); 56 57 mipi_dsi_generic_write_seq_multi(ctx, 0xb5, 0x03, 0x7f, 0x00, 0x80, 0xc7, 0x00); 58 mipi_dsi_generic_write_seq_multi(ctx, 0xb7, 0x66, 0xf6, 0x46, 0x9f, 0x90, 0x99, 59 0xff, 0x80, 0x6d, 0x01); 60 61 /* Gamma R */ 62 mipi_dsi_generic_write_seq_multi(ctx, 0xf9, 0x04); 63 mipi_dsi_generic_write_seq_multi(ctx, 0xfa, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, 64 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); 65 mipi_dsi_generic_write_seq_multi(ctx, 0xfb, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, 66 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); 67 68 /* Gamma G */ 69 mipi_dsi_generic_write_seq_multi(ctx, 0xf9, 0x02); 70 mipi_dsi_generic_write_seq_multi(ctx, 0xfa, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, 71 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); 72 mipi_dsi_generic_write_seq_multi(ctx, 0xfb, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, 73 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); 74 75 /* Gamma B */ 76 mipi_dsi_generic_write_seq_multi(ctx, 0xf9, 0x01); 77 mipi_dsi_generic_write_seq_multi(ctx, 0xfa, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, 78 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); 79 mipi_dsi_generic_write_seq_multi(ctx, 0xfb, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, 80 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); 81 82 /* Gamma W */ 83 mipi_dsi_generic_write_seq_multi(ctx, 0xf9, 0x20); 84 mipi_dsi_generic_write_seq_multi(ctx, 0xfa, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, 85 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); 86 mipi_dsi_generic_write_seq_multi(ctx, 0xfb, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, 87 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); 88 89 mipi_dsi_generic_write_seq_multi(ctx, 0x53, 0x2c); 90 mipi_dsi_generic_write_seq_multi(ctx, 0x35, 0x00); 91 } 92 93 static int mot_panel_prepare(struct drm_panel *panel) 94 { 95 struct mot_panel *priv = to_mot_panel(panel); 96 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 97 struct device *dev = panel->dev; 98 int ret; 99 100 ret = regulator_bulk_enable(ARRAY_SIZE(mot_panel_supplies), priv->supplies); 101 if (ret < 0) { 102 dev_err(dev, "failed to enable power supplies: %d\n", ret); 103 return ret; 104 } 105 106 mot_panel_reset(priv); 107 108 mipi_dsi_generic_write_seq_multi(&ctx, 0xf0, 0x5a, 0x5a); 109 mipi_dsi_generic_write_seq_multi(&ctx, 0xf1, 0x5a, 0x5a); 110 mipi_dsi_generic_write_seq_multi(&ctx, 0xd0, 0x8e); 111 112 mot_es2(&ctx); 113 114 mipi_dsi_dcs_set_display_on_multi(&ctx); 115 mipi_dsi_msleep(&ctx, 20); 116 117 return ctx.accum_err; 118 } 119 120 static int mot_panel_disable(struct drm_panel *panel) 121 { 122 struct mot_panel *priv = to_mot_panel(panel); 123 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 124 125 mipi_dsi_dcs_set_display_off_multi(&ctx); 126 mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); 127 mipi_dsi_msleep(&ctx, 70); 128 129 return ctx.accum_err; 130 } 131 132 static int mot_panel_unprepare(struct drm_panel *panel) 133 { 134 struct mot_panel *priv = to_mot_panel(panel); 135 136 usleep_range(10000, 11000); 137 138 gpiod_set_value_cansleep(priv->reset_gpio, 1); 139 usleep_range(5000, 6000); 140 141 regulator_bulk_disable(ARRAY_SIZE(mot_panel_supplies), priv->supplies); 142 143 return 0; 144 } 145 146 static const struct drm_display_mode mot_panel_mode = { 147 .clock = (540 + 32 + 32 + 16) * (960 + 12 + 12 + 8) * 60 / 1000, 148 .hdisplay = 540, 149 .hsync_start = 540 + 32, 150 .hsync_end = 540 + 32 + 32, 151 .htotal = 540 + 32 + 32 + 16, 152 .vdisplay = 960, 153 .vsync_start = 960 + 12, 154 .vsync_end = 960 + 12 + 12, 155 .vtotal = 960 + 12 + 12 + 8, 156 .width_mm = 51, 157 .height_mm = 91, 158 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 159 }; 160 161 static int mot_panel_get_modes(struct drm_panel *panel, 162 struct drm_connector *connector) 163 { 164 return drm_connector_helper_get_modes_fixed(connector, &mot_panel_mode); 165 } 166 167 static const struct drm_panel_funcs mot_panel_panel_funcs = { 168 .prepare = mot_panel_prepare, 169 .disable = mot_panel_disable, 170 .unprepare = mot_panel_unprepare, 171 .get_modes = mot_panel_get_modes, 172 }; 173 174 static int mot_panel_probe(struct mipi_dsi_device *dsi) 175 { 176 struct device *dev = &dsi->dev; 177 struct mot_panel *priv; 178 int ret; 179 180 priv = devm_drm_panel_alloc(dev, struct mot_panel, panel, 181 &mot_panel_panel_funcs, 182 DRM_MODE_CONNECTOR_DSI); 183 if (IS_ERR(priv)) 184 return PTR_ERR(priv); 185 186 ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(mot_panel_supplies), 187 mot_panel_supplies, &priv->supplies); 188 if (ret < 0) 189 return dev_err_probe(dev, ret, "failed to get supplies\n"); 190 191 priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 192 GPIOD_OUT_HIGH); 193 if (IS_ERR(priv->reset_gpio)) 194 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), 195 "failed to get reset gpios\n"); 196 197 priv->dsi = dsi; 198 mipi_dsi_set_drvdata(dsi, priv); 199 200 dsi->lanes = 2; 201 dsi->format = MIPI_DSI_FMT_RGB888; 202 dsi->mode_flags = MIPI_DSI_MODE_LPM; 203 204 ret = drm_panel_of_backlight(&priv->panel); 205 if (ret < 0) 206 return dev_err_probe(dev, ret, "failed to get backlight\n"); 207 208 drm_panel_add(&priv->panel); 209 210 ret = devm_mipi_dsi_attach(dev, dsi); 211 if (ret < 0) { 212 drm_panel_remove(&priv->panel); 213 return dev_err_probe(dev, ret, "failed to attach to DSI host\n"); 214 } 215 216 return 0; 217 } 218 219 static void mot_panel_remove(struct mipi_dsi_device *dsi) 220 { 221 struct mot_panel *priv = mipi_dsi_get_drvdata(dsi); 222 223 drm_panel_remove(&priv->panel); 224 } 225 226 static const struct of_device_id mot_panel_of_match[] = { 227 { .compatible = "motorola,mot-panel" }, 228 { /* sentinel */ } 229 }; 230 MODULE_DEVICE_TABLE(of, mot_panel_of_match); 231 232 static struct mipi_dsi_driver mot_panel_driver = { 233 .driver = { 234 .name = "panel-motorola-mot", 235 .of_match_table = mot_panel_of_match, 236 }, 237 .probe = mot_panel_probe, 238 .remove = mot_panel_remove, 239 }; 240 module_mipi_dsi_driver(mot_panel_driver); 241 242 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 243 MODULE_DESCRIPTION("Motorola MOT panel driver"); 244 MODULE_LICENSE("GPL"); 245