1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2018 Amarula Solutions 4 * Author: Jagan Teki <jagan@amarulasolutions.com> 5 */ 6 7 #include <drm/drm_mipi_dsi.h> 8 #include <drm/drm_modes.h> 9 #include <drm/drm_panel.h> 10 #include <drm/drm_print.h> 11 12 #include <linux/gpio/consumer.h> 13 #include <linux/delay.h> 14 #include <linux/module.h> 15 #include <linux/of_device.h> 16 #include <linux/regulator/consumer.h> 17 18 #define FEIYANG_INIT_CMD_LEN 2 19 20 struct feiyang { 21 struct drm_panel panel; 22 struct mipi_dsi_device *dsi; 23 24 struct regulator *dvdd; 25 struct regulator *avdd; 26 struct gpio_desc *reset; 27 }; 28 29 static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel) 30 { 31 return container_of(panel, struct feiyang, panel); 32 } 33 34 struct feiyang_init_cmd { 35 u8 data[FEIYANG_INIT_CMD_LEN]; 36 }; 37 38 static const struct feiyang_init_cmd feiyang_init_cmds[] = { 39 { .data = { 0x80, 0x58 } }, 40 { .data = { 0x81, 0x47 } }, 41 { .data = { 0x82, 0xD4 } }, 42 { .data = { 0x83, 0x88 } }, 43 { .data = { 0x84, 0xA9 } }, 44 { .data = { 0x85, 0xC3 } }, 45 { .data = { 0x86, 0x82 } }, 46 }; 47 48 static int feiyang_prepare(struct drm_panel *panel) 49 { 50 struct feiyang *ctx = panel_to_feiyang(panel); 51 struct mipi_dsi_device *dsi = ctx->dsi; 52 unsigned int i; 53 int ret; 54 55 ret = regulator_enable(ctx->dvdd); 56 if (ret) 57 return ret; 58 59 /* T1 (dvdd start + dvdd rise) 0 < T1 <= 10ms */ 60 msleep(10); 61 62 ret = regulator_enable(ctx->avdd); 63 if (ret) 64 return ret; 65 66 /* T3 (dvdd rise + avdd start + avdd rise) T3 >= 20ms */ 67 msleep(20); 68 69 gpiod_set_value(ctx->reset, 0); 70 71 /* 72 * T5 + T6 (avdd rise + video & logic signal rise) 73 * T5 >= 10ms, 0 < T6 <= 10ms 74 */ 75 msleep(20); 76 77 gpiod_set_value(ctx->reset, 1); 78 79 /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */ 80 msleep(200); 81 82 for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) { 83 const struct feiyang_init_cmd *cmd = 84 &feiyang_init_cmds[i]; 85 86 ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, 87 FEIYANG_INIT_CMD_LEN); 88 if (ret < 0) 89 return ret; 90 } 91 92 return 0; 93 } 94 95 static int feiyang_enable(struct drm_panel *panel) 96 { 97 struct feiyang *ctx = panel_to_feiyang(panel); 98 99 /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */ 100 msleep(200); 101 102 mipi_dsi_dcs_set_display_on(ctx->dsi); 103 104 return 0; 105 } 106 107 static int feiyang_disable(struct drm_panel *panel) 108 { 109 struct feiyang *ctx = panel_to_feiyang(panel); 110 111 return mipi_dsi_dcs_set_display_off(ctx->dsi); 112 } 113 114 static int feiyang_unprepare(struct drm_panel *panel) 115 { 116 struct feiyang *ctx = panel_to_feiyang(panel); 117 int ret; 118 119 ret = mipi_dsi_dcs_set_display_off(ctx->dsi); 120 if (ret < 0) 121 DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n", 122 ret); 123 124 ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); 125 if (ret < 0) 126 DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n", 127 ret); 128 129 /* T13 (backlight fall + video & logic signal fall) T13 >= 200ms */ 130 msleep(200); 131 132 gpiod_set_value(ctx->reset, 0); 133 134 regulator_disable(ctx->avdd); 135 136 /* T11 (dvdd rise to fall) 0 < T11 <= 10ms */ 137 msleep(10); 138 139 regulator_disable(ctx->dvdd); 140 141 return 0; 142 } 143 144 static const struct drm_display_mode feiyang_default_mode = { 145 .clock = 55000, 146 147 .hdisplay = 1024, 148 .hsync_start = 1024 + 310, 149 .hsync_end = 1024 + 310 + 20, 150 .htotal = 1024 + 310 + 20 + 90, 151 152 .vdisplay = 600, 153 .vsync_start = 600 + 12, 154 .vsync_end = 600 + 12 + 2, 155 .vtotal = 600 + 12 + 2 + 21, 156 .vrefresh = 60, 157 158 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 159 }; 160 161 static int feiyang_get_modes(struct drm_panel *panel, 162 struct drm_connector *connector) 163 { 164 struct feiyang *ctx = panel_to_feiyang(panel); 165 struct drm_display_mode *mode; 166 167 mode = drm_mode_duplicate(connector->dev, &feiyang_default_mode); 168 if (!mode) { 169 DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n", 170 feiyang_default_mode.hdisplay, 171 feiyang_default_mode.vdisplay, 172 feiyang_default_mode.vrefresh); 173 return -ENOMEM; 174 } 175 176 drm_mode_set_name(mode); 177 178 drm_mode_probed_add(connector, mode); 179 180 return 1; 181 } 182 183 static const struct drm_panel_funcs feiyang_funcs = { 184 .disable = feiyang_disable, 185 .unprepare = feiyang_unprepare, 186 .prepare = feiyang_prepare, 187 .enable = feiyang_enable, 188 .get_modes = feiyang_get_modes, 189 }; 190 191 static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) 192 { 193 struct feiyang *ctx; 194 int ret; 195 196 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); 197 if (!ctx) 198 return -ENOMEM; 199 200 mipi_dsi_set_drvdata(dsi, ctx); 201 ctx->dsi = dsi; 202 203 drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs, 204 DRM_MODE_CONNECTOR_DSI); 205 206 ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); 207 if (IS_ERR(ctx->dvdd)) { 208 DRM_DEV_ERROR(&dsi->dev, "Couldn't get dvdd regulator\n"); 209 return PTR_ERR(ctx->dvdd); 210 } 211 212 ctx->avdd = devm_regulator_get(&dsi->dev, "avdd"); 213 if (IS_ERR(ctx->avdd)) { 214 DRM_DEV_ERROR(&dsi->dev, "Couldn't get avdd regulator\n"); 215 return PTR_ERR(ctx->avdd); 216 } 217 218 ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); 219 if (IS_ERR(ctx->reset)) { 220 DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n"); 221 return PTR_ERR(ctx->reset); 222 } 223 224 ret = drm_panel_of_backlight(&ctx->panel); 225 if (ret) 226 return ret; 227 228 ret = drm_panel_add(&ctx->panel); 229 if (ret < 0) 230 return ret; 231 232 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST; 233 dsi->format = MIPI_DSI_FMT_RGB888; 234 dsi->lanes = 4; 235 236 return mipi_dsi_attach(dsi); 237 } 238 239 static int feiyang_dsi_remove(struct mipi_dsi_device *dsi) 240 { 241 struct feiyang *ctx = mipi_dsi_get_drvdata(dsi); 242 243 mipi_dsi_detach(dsi); 244 drm_panel_remove(&ctx->panel); 245 246 return 0; 247 } 248 249 static const struct of_device_id feiyang_of_match[] = { 250 { .compatible = "feiyang,fy07024di26a30d", }, 251 { /* sentinel */ } 252 }; 253 MODULE_DEVICE_TABLE(of, feiyang_of_match); 254 255 static struct mipi_dsi_driver feiyang_driver = { 256 .probe = feiyang_dsi_probe, 257 .remove = feiyang_dsi_remove, 258 .driver = { 259 .name = "feiyang-fy07024di26a30d", 260 .of_match_table = feiyang_of_match, 261 }, 262 }; 263 module_mipi_dsi_driver(feiyang_driver); 264 265 MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); 266 MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel"); 267 MODULE_LICENSE("GPL"); 268