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 return drm_panel_add(&osd101t2587->base); 168 } 169 170 static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi) 171 { 172 struct osd101t2587_panel *osd101t2587; 173 const struct of_device_id *id; 174 int ret; 175 176 id = of_match_node(osd101t2587_of_match, dsi->dev.of_node); 177 if (!id) 178 return -ENODEV; 179 180 dsi->lanes = 4; 181 dsi->format = MIPI_DSI_FMT_RGB888; 182 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 183 MIPI_DSI_MODE_VIDEO_BURST | 184 MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 185 MIPI_DSI_MODE_EOT_PACKET; 186 187 osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL); 188 if (!osd101t2587) 189 return -ENOMEM; 190 191 mipi_dsi_set_drvdata(dsi, osd101t2587); 192 193 osd101t2587->dsi = dsi; 194 osd101t2587->default_mode = id->data; 195 196 ret = osd101t2587_panel_add(osd101t2587); 197 if (ret < 0) 198 return ret; 199 200 ret = mipi_dsi_attach(dsi); 201 if (ret) 202 drm_panel_remove(&osd101t2587->base); 203 204 return ret; 205 } 206 207 static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) 208 { 209 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 210 int ret; 211 212 ret = drm_panel_disable(&osd101t2587->base); 213 if (ret < 0) 214 dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret); 215 216 drm_panel_unprepare(&osd101t2587->base); 217 drm_panel_remove(&osd101t2587->base); 218 219 ret = mipi_dsi_detach(dsi); 220 if (ret < 0) 221 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 222 223 return ret; 224 } 225 226 static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi) 227 { 228 struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 229 230 drm_panel_disable(&osd101t2587->base); 231 drm_panel_unprepare(&osd101t2587->base); 232 } 233 234 static struct mipi_dsi_driver osd101t2587_panel_driver = { 235 .driver = { 236 .name = "panel-osd-osd101t2587-53ts", 237 .of_match_table = osd101t2587_of_match, 238 }, 239 .probe = osd101t2587_panel_probe, 240 .remove = osd101t2587_panel_remove, 241 .shutdown = osd101t2587_panel_shutdown, 242 }; 243 module_mipi_dsi_driver(osd101t2587_panel_driver); 244 245 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 246 MODULE_DESCRIPTION("OSD101T2587-53TS DSI panel"); 247 MODULE_LICENSE("GPL v2"); 248