1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com 4 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 5 */ 6 7 #include <linux/module.h> 8 #include <linux/of.h> 9 #include <linux/regulator/consumer.h> 10 11 #include <drm/drm_crtc.h> 12 #include <drm/drm_device.h> 13 #include <drm/drm_mipi_dsi.h> 14 #include <drm/drm_panel.h> 15 16 #include <video/mipi_display.h> 17 18 struct osd101t2587_panel { 19 struct drm_panel base; 20 struct mipi_dsi_device *dsi; 21 22 struct regulator *supply; 23 24 bool prepared; 25 bool enabled; 26 27 const struct drm_display_mode *default_mode; 28 }; 29 30 static inline struct osd101t2587_panel *ti_osd_panel(struct drm_panel *panel) 31 { 32 return container_of(panel, struct osd101t2587_panel, base); 33 } 34 35 static int osd101t2587_panel_disable(struct drm_panel *panel) 36 { 37 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 38 int ret; 39 40 if (!osd101t2587->enabled) 41 return 0; 42 43 ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi); 44 45 osd101t2587->enabled = false; 46 47 return ret; 48 } 49 50 static int osd101t2587_panel_unprepare(struct drm_panel *panel) 51 { 52 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 53 54 if (!osd101t2587->prepared) 55 return 0; 56 57 regulator_disable(osd101t2587->supply); 58 osd101t2587->prepared = false; 59 60 return 0; 61 } 62 63 static int osd101t2587_panel_prepare(struct drm_panel *panel) 64 { 65 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 66 int ret; 67 68 if (osd101t2587->prepared) 69 return 0; 70 71 ret = regulator_enable(osd101t2587->supply); 72 if (!ret) 73 osd101t2587->prepared = true; 74 75 return ret; 76 } 77 78 static int osd101t2587_panel_enable(struct drm_panel *panel) 79 { 80 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 81 int ret; 82 83 if (osd101t2587->enabled) 84 return 0; 85 86 ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi); 87 if (ret) 88 return ret; 89 90 osd101t2587->enabled = true; 91 92 return ret; 93 } 94 95 static const struct drm_display_mode default_mode_osd101t2587 = { 96 .clock = 164400, 97 .hdisplay = 1920, 98 .hsync_start = 1920 + 152, 99 .hsync_end = 1920 + 152 + 52, 100 .htotal = 1920 + 152 + 52 + 20, 101 .vdisplay = 1200, 102 .vsync_start = 1200 + 24, 103 .vsync_end = 1200 + 24 + 6, 104 .vtotal = 1200 + 24 + 6 + 48, 105 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 106 }; 107 108 static int osd101t2587_panel_get_modes(struct drm_panel *panel, 109 struct drm_connector *connector) 110 { 111 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 112 struct drm_display_mode *mode; 113 114 mode = drm_mode_duplicate(connector->dev, osd101t2587->default_mode); 115 if (!mode) { 116 dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", 117 osd101t2587->default_mode->hdisplay, 118 osd101t2587->default_mode->vdisplay, 119 drm_mode_vrefresh(osd101t2587->default_mode)); 120 return -ENOMEM; 121 } 122 123 drm_mode_set_name(mode); 124 125 drm_mode_probed_add(connector, mode); 126 127 connector->display_info.width_mm = 217; 128 connector->display_info.height_mm = 136; 129 130 return 1; 131 } 132 133 static const struct drm_panel_funcs osd101t2587_panel_funcs = { 134 .disable = osd101t2587_panel_disable, 135 .unprepare = osd101t2587_panel_unprepare, 136 .prepare = osd101t2587_panel_prepare, 137 .enable = osd101t2587_panel_enable, 138 .get_modes = osd101t2587_panel_get_modes, 139 }; 140 141 static const struct of_device_id osd101t2587_of_match[] = { 142 { 143 .compatible = "osddisplays,osd101t2587-53ts", 144 .data = &default_mode_osd101t2587, 145 }, { 146 /* sentinel */ 147 } 148 }; 149 MODULE_DEVICE_TABLE(of, osd101t2587_of_match); 150 151 static int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587) 152 { 153 struct device *dev = &osd101t2587->dsi->dev; 154 int ret; 155 156 osd101t2587->supply = devm_regulator_get(dev, "power"); 157 if (IS_ERR(osd101t2587->supply)) 158 return PTR_ERR(osd101t2587->supply); 159 160 drm_panel_init(&osd101t2587->base, &osd101t2587->dsi->dev, 161 &osd101t2587_panel_funcs, DRM_MODE_CONNECTOR_DSI); 162 163 ret = drm_panel_of_backlight(&osd101t2587->base); 164 if (ret) 165 return ret; 166 167 drm_panel_add(&osd101t2587->base); 168 169 return 0; 170 } 171 172 static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi) 173 { 174 struct osd101t2587_panel *osd101t2587; 175 const struct of_device_id *id; 176 int ret; 177 178 id = of_match_node(osd101t2587_of_match, dsi->dev.of_node); 179 if (!id) 180 return -ENODEV; 181 182 dsi->lanes = 4; 183 dsi->format = MIPI_DSI_FMT_RGB888; 184 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 185 MIPI_DSI_MODE_VIDEO_BURST | 186 MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 187 MIPI_DSI_MODE_NO_EOT_PACKET; 188 189 osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL); 190 if (!osd101t2587) 191 return -ENOMEM; 192 193 mipi_dsi_set_drvdata(dsi, osd101t2587); 194 195 osd101t2587->dsi = dsi; 196 osd101t2587->default_mode = id->data; 197 198 ret = osd101t2587_panel_add(osd101t2587); 199 if (ret < 0) 200 return ret; 201 202 ret = mipi_dsi_attach(dsi); 203 if (ret) 204 drm_panel_remove(&osd101t2587->base); 205 206 return ret; 207 } 208 209 static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) 210 { 211 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 212 int ret; 213 214 ret = drm_panel_disable(&osd101t2587->base); 215 if (ret < 0) 216 dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret); 217 218 drm_panel_unprepare(&osd101t2587->base); 219 drm_panel_remove(&osd101t2587->base); 220 221 ret = mipi_dsi_detach(dsi); 222 if (ret < 0) 223 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 224 225 return ret; 226 } 227 228 static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi) 229 { 230 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 231 232 drm_panel_disable(&osd101t2587->base); 233 drm_panel_unprepare(&osd101t2587->base); 234 } 235 236 static struct mipi_dsi_driver osd101t2587_panel_driver = { 237 .driver = { 238 .name = "panel-osd-osd101t2587-53ts", 239 .of_match_table = osd101t2587_of_match, 240 }, 241 .probe = osd101t2587_panel_probe, 242 .remove = osd101t2587_panel_remove, 243 .shutdown = osd101t2587_panel_shutdown, 244 }; 245 module_mipi_dsi_driver(osd101t2587_panel_driver); 246 247 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 248 MODULE_DESCRIPTION("OSD101T2587-53TS DSI panel"); 249 MODULE_LICENSE("GPL v2"); 250