18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com 48c2ecf20Sopenharmony_ci * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct osd101t2587_panel { 198c2ecf20Sopenharmony_ci struct drm_panel base; 208c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci struct regulator *supply; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci bool prepared; 258c2ecf20Sopenharmony_ci bool enabled; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci const struct drm_display_mode *default_mode; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline struct osd101t2587_panel *ti_osd_panel(struct drm_panel *panel) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return container_of(panel, struct osd101t2587_panel, base); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int osd101t2587_panel_disable(struct drm_panel *panel) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 388c2ecf20Sopenharmony_ci int ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!osd101t2587->enabled) 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci osd101t2587->enabled = false; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int osd101t2587_panel_unprepare(struct drm_panel *panel) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!osd101t2587->prepared) 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci regulator_disable(osd101t2587->supply); 588c2ecf20Sopenharmony_ci osd101t2587->prepared = false; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int osd101t2587_panel_prepare(struct drm_panel *panel) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 668c2ecf20Sopenharmony_ci int ret; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (osd101t2587->prepared) 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = regulator_enable(osd101t2587->supply); 728c2ecf20Sopenharmony_ci if (!ret) 738c2ecf20Sopenharmony_ci osd101t2587->prepared = true; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int osd101t2587_panel_enable(struct drm_panel *panel) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 818c2ecf20Sopenharmony_ci int ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (osd101t2587->enabled) 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi); 878c2ecf20Sopenharmony_ci if (ret) 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci osd101t2587->enabled = true; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const struct drm_display_mode default_mode_osd101t2587 = { 968c2ecf20Sopenharmony_ci .clock = 164400, 978c2ecf20Sopenharmony_ci .hdisplay = 1920, 988c2ecf20Sopenharmony_ci .hsync_start = 1920 + 152, 998c2ecf20Sopenharmony_ci .hsync_end = 1920 + 152 + 52, 1008c2ecf20Sopenharmony_ci .htotal = 1920 + 152 + 52 + 20, 1018c2ecf20Sopenharmony_ci .vdisplay = 1200, 1028c2ecf20Sopenharmony_ci .vsync_start = 1200 + 24, 1038c2ecf20Sopenharmony_ci .vsync_end = 1200 + 24 + 6, 1048c2ecf20Sopenharmony_ci .vtotal = 1200 + 24 + 6 + 48, 1058c2ecf20Sopenharmony_ci .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int osd101t2587_panel_get_modes(struct drm_panel *panel, 1098c2ecf20Sopenharmony_ci struct drm_connector *connector) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel); 1128c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, osd101t2587->default_mode); 1158c2ecf20Sopenharmony_ci if (!mode) { 1168c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", 1178c2ecf20Sopenharmony_ci osd101t2587->default_mode->hdisplay, 1188c2ecf20Sopenharmony_ci osd101t2587->default_mode->vdisplay, 1198c2ecf20Sopenharmony_ci drm_mode_vrefresh(osd101t2587->default_mode)); 1208c2ecf20Sopenharmony_ci return -ENOMEM; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci connector->display_info.width_mm = 217; 1288c2ecf20Sopenharmony_ci connector->display_info.height_mm = 136; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 1; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs osd101t2587_panel_funcs = { 1348c2ecf20Sopenharmony_ci .disable = osd101t2587_panel_disable, 1358c2ecf20Sopenharmony_ci .unprepare = osd101t2587_panel_unprepare, 1368c2ecf20Sopenharmony_ci .prepare = osd101t2587_panel_prepare, 1378c2ecf20Sopenharmony_ci .enable = osd101t2587_panel_enable, 1388c2ecf20Sopenharmony_ci .get_modes = osd101t2587_panel_get_modes, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct of_device_id osd101t2587_of_match[] = { 1428c2ecf20Sopenharmony_ci { 1438c2ecf20Sopenharmony_ci .compatible = "osddisplays,osd101t2587-53ts", 1448c2ecf20Sopenharmony_ci .data = &default_mode_osd101t2587, 1458c2ecf20Sopenharmony_ci }, { 1468c2ecf20Sopenharmony_ci /* sentinel */ 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, osd101t2587_of_match); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct device *dev = &osd101t2587->dsi->dev; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci osd101t2587->supply = devm_regulator_get(dev, "power"); 1578c2ecf20Sopenharmony_ci if (IS_ERR(osd101t2587->supply)) 1588c2ecf20Sopenharmony_ci return PTR_ERR(osd101t2587->supply); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci drm_panel_init(&osd101t2587->base, &osd101t2587->dsi->dev, 1618c2ecf20Sopenharmony_ci &osd101t2587_panel_funcs, DRM_MODE_CONNECTOR_DSI); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = drm_panel_of_backlight(&osd101t2587->base); 1648c2ecf20Sopenharmony_ci if (ret) 1658c2ecf20Sopenharmony_ci return ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci drm_panel_add(&osd101t2587->base); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int osd101t2587_panel_probe(struct mipi_dsi_device *dsi) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587; 1758c2ecf20Sopenharmony_ci const struct of_device_id *id; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci id = of_match_node(osd101t2587_of_match, dsi->dev.of_node); 1798c2ecf20Sopenharmony_ci if (!id) 1808c2ecf20Sopenharmony_ci return -ENODEV; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dsi->lanes = 4; 1838c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 1848c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 1858c2ecf20Sopenharmony_ci MIPI_DSI_MODE_VIDEO_BURST | 1868c2ecf20Sopenharmony_ci MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 1878c2ecf20Sopenharmony_ci MIPI_DSI_MODE_EOT_PACKET; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL); 1908c2ecf20Sopenharmony_ci if (!osd101t2587) 1918c2ecf20Sopenharmony_ci return -ENOMEM; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, osd101t2587); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci osd101t2587->dsi = dsi; 1968c2ecf20Sopenharmony_ci osd101t2587->default_mode = id->data; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = osd101t2587_panel_add(osd101t2587); 1998c2ecf20Sopenharmony_ci if (ret < 0) 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 2038c2ecf20Sopenharmony_ci if (ret) 2048c2ecf20Sopenharmony_ci drm_panel_remove(&osd101t2587->base); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ret = drm_panel_disable(&osd101t2587->base); 2158c2ecf20Sopenharmony_ci if (ret < 0) 2168c2ecf20Sopenharmony_ci dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci drm_panel_unprepare(&osd101t2587->base); 2198c2ecf20Sopenharmony_ci drm_panel_remove(&osd101t2587->base); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = mipi_dsi_detach(dsi); 2228c2ecf20Sopenharmony_ci if (ret < 0) 2238c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci drm_panel_disable(&osd101t2587->base); 2338c2ecf20Sopenharmony_ci drm_panel_unprepare(&osd101t2587->base); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver osd101t2587_panel_driver = { 2378c2ecf20Sopenharmony_ci .driver = { 2388c2ecf20Sopenharmony_ci .name = "panel-osd-osd101t2587-53ts", 2398c2ecf20Sopenharmony_ci .of_match_table = osd101t2587_of_match, 2408c2ecf20Sopenharmony_ci }, 2418c2ecf20Sopenharmony_ci .probe = osd101t2587_panel_probe, 2428c2ecf20Sopenharmony_ci .remove = osd101t2587_panel_remove, 2438c2ecf20Sopenharmony_ci .shutdown = osd101t2587_panel_shutdown, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(osd101t2587_panel_driver); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); 2488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OSD101T2587-53TS DSI panel"); 2498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 250