1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2019 Texas Instruments Incorporated - http://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 .vrefresh = 60, 106 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 107 }; 108 109 static int osd101t2587_panel_get_modes(struct drm_panel *panel, 110 struct drm_connector *connector) 111 { 112 struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 113 struct drm_display_mode *mode; 114 115 mode = drm_mode_duplicate(connector->dev, osd101t2587->default_mode); 116 if (!mode) { 117 dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", 118 osd101t2587->default_mode->hdisplay, 119 osd101t2587->default_mode->vdisplay, 120 osd101t2587->default_mode->vrefresh); 121 return -ENOMEM; 122 } 123 124 drm_mode_set_name(mode); 125 126 drm_mode_probed_add(connector, mode); 127 128 connector->display_info.width_mm = 217; 129 connector->display_info.height_mm = 136; 130 131 return 1; 132 } 133 134 static const struct drm_panel_funcs osd101t2587_panel_funcs = { 135 .disable = osd101t2587_panel_disable, 136 .unprepare = osd101t2587_panel_unprepare, 137 .prepare = osd101t2587_panel_prepare, 138 .enable = osd101t2587_panel_enable, 139 .get_modes = osd101t2587_panel_get_modes, 140 }; 141 142 static const struct of_device_id osd101t2587_of_match[] = { 143 { 144 .compatible = "osddisplays,osd101t2587-53ts", 145 .data = &default_mode_osd101t2587, 146 }, { 147 /* sentinel */ 148 } 149 }; 150 MODULE_DEVICE_TABLE(of, osd101t2587_of_match); 151 152 static int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587) 153 { 154 struct device *dev = &osd101t2587->dsi->dev; 155 int ret; 156 157 osd101t2587->supply = devm_regulator_get(dev, "power"); 158 if (IS_ERR(osd101t2587->supply)) 159 return PTR_ERR(osd101t2587->supply); 160 161 drm_panel_init(&osd101t2587->base, &osd101t2587->dsi->dev, 162 &osd101t2587_panel_funcs, DRM_MODE_CONNECTOR_DSI); 163 164 ret = drm_panel_of_backlight(&osd101t2587->base); 165 if (ret) 166 return ret; 167 168 return drm_panel_add(&osd101t2587->base); 169 } 170 171 static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi) 172 { 173 struct osd101t2587_panel *osd101t2587; 174 const struct of_device_id *id; 175 int ret; 176 177 id = of_match_node(osd101t2587_of_match, dsi->dev.of_node); 178 if (!id) 179 return -ENODEV; 180 181 dsi->lanes = 4; 182 dsi->format = MIPI_DSI_FMT_RGB888; 183 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 184 MIPI_DSI_MODE_VIDEO_BURST | 185 MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 186 MIPI_DSI_MODE_EOT_PACKET; 187 188 osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL); 189 if (!osd101t2587) 190 return -ENOMEM; 191 192 mipi_dsi_set_drvdata(dsi, osd101t2587); 193 194 osd101t2587->dsi = dsi; 195 osd101t2587->default_mode = id->data; 196 197 ret = osd101t2587_panel_add(osd101t2587); 198 if (ret < 0) 199 return ret; 200 201 ret = mipi_dsi_attach(dsi); 202 if (ret) 203 drm_panel_remove(&osd101t2587->base); 204 205 return ret; 206 } 207 208 static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) 209 { 210 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 211 int ret; 212 213 ret = drm_panel_disable(&osd101t2587->base); 214 if (ret < 0) 215 dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret); 216 217 drm_panel_unprepare(&osd101t2587->base); 218 drm_panel_remove(&osd101t2587->base); 219 220 ret = mipi_dsi_detach(dsi); 221 if (ret < 0) 222 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 223 224 return ret; 225 } 226 227 static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi) 228 { 229 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 230 231 drm_panel_disable(&osd101t2587->base); 232 drm_panel_unprepare(&osd101t2587->base); 233 } 234 235 static struct mipi_dsi_driver osd101t2587_panel_driver = { 236 .driver = { 237 .name = "panel-osd-osd101t2587-53ts", 238 .of_match_table = osd101t2587_of_match, 239 }, 240 .probe = osd101t2587_panel_probe, 241 .remove = osd101t2587_panel_remove, 242 .shutdown = osd101t2587_panel_shutdown, 243 }; 244 module_mipi_dsi_driver(osd101t2587_panel_driver); 245 246 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 247 MODULE_DESCRIPTION("OSD101T2587-53TS DSI panel"); 248 MODULE_LICENSE("GPL v2"); 249