18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019, Bridge Systems BV 48c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019, Bootlin 58c2ecf20Sopenharmony_ci * Copyright (C) 2017, Free Electrons 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file based on panel-ilitek-ili9881c.c 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/fb.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/media-bus-format.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <drm/drm_connector.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct rb070d30_panel { 288c2ecf20Sopenharmony_ci struct drm_panel panel; 298c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi; 308c2ecf20Sopenharmony_ci struct regulator *supply; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci struct { 338c2ecf20Sopenharmony_ci struct gpio_desc *power; 348c2ecf20Sopenharmony_ci struct gpio_desc *reset; 358c2ecf20Sopenharmony_ci struct gpio_desc *updn; 368c2ecf20Sopenharmony_ci struct gpio_desc *shlr; 378c2ecf20Sopenharmony_ci } gpios; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline struct rb070d30_panel *panel_to_rb070d30_panel(struct drm_panel *panel) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return container_of(panel, struct rb070d30_panel, panel); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int rb070d30_panel_prepare(struct drm_panel *panel) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); 488c2ecf20Sopenharmony_ci int ret; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->supply); 518c2ecf20Sopenharmony_ci if (ret < 0) { 528c2ecf20Sopenharmony_ci dev_err(&ctx->dsi->dev, "Failed to enable supply: %d\n", ret); 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci msleep(20); 578c2ecf20Sopenharmony_ci gpiod_set_value(ctx->gpios.power, 1); 588c2ecf20Sopenharmony_ci msleep(20); 598c2ecf20Sopenharmony_ci gpiod_set_value(ctx->gpios.reset, 1); 608c2ecf20Sopenharmony_ci msleep(20); 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int rb070d30_panel_unprepare(struct drm_panel *panel) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci gpiod_set_value(ctx->gpios.reset, 0); 698c2ecf20Sopenharmony_ci gpiod_set_value(ctx->gpios.power, 0); 708c2ecf20Sopenharmony_ci regulator_disable(ctx->supply); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int rb070d30_panel_enable(struct drm_panel *panel) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); 818c2ecf20Sopenharmony_ci if (ret) 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int rb070d30_panel_disable(struct drm_panel *panel) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Default timings */ 958c2ecf20Sopenharmony_cistatic const struct drm_display_mode default_mode = { 968c2ecf20Sopenharmony_ci .clock = 51206, 978c2ecf20Sopenharmony_ci .hdisplay = 1024, 988c2ecf20Sopenharmony_ci .hsync_start = 1024 + 160, 998c2ecf20Sopenharmony_ci .hsync_end = 1024 + 160 + 80, 1008c2ecf20Sopenharmony_ci .htotal = 1024 + 160 + 80 + 80, 1018c2ecf20Sopenharmony_ci .vdisplay = 600, 1028c2ecf20Sopenharmony_ci .vsync_start = 600 + 12, 1038c2ecf20Sopenharmony_ci .vsync_end = 600 + 12 + 10, 1048c2ecf20Sopenharmony_ci .vtotal = 600 + 12 + 10 + 13, 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci .width_mm = 154, 1078c2ecf20Sopenharmony_ci .height_mm = 85, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int rb070d30_panel_get_modes(struct drm_panel *panel, 1118c2ecf20Sopenharmony_ci struct drm_connector *connector) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); 1148c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 1158c2ecf20Sopenharmony_ci static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 1188c2ecf20Sopenharmony_ci if (!mode) { 1198c2ecf20Sopenharmony_ci dev_err(&ctx->dsi->dev, "Failed to add mode " DRM_MODE_FMT "\n", 1208c2ecf20Sopenharmony_ci DRM_MODE_ARG(&default_mode)); 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 1278c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci connector->display_info.bpc = 8; 1308c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 1318c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 1328c2ecf20Sopenharmony_ci drm_display_info_set_bus_formats(&connector->display_info, 1338c2ecf20Sopenharmony_ci &bus_format, 1); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 1; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs rb070d30_panel_funcs = { 1398c2ecf20Sopenharmony_ci .get_modes = rb070d30_panel_get_modes, 1408c2ecf20Sopenharmony_ci .prepare = rb070d30_panel_prepare, 1418c2ecf20Sopenharmony_ci .enable = rb070d30_panel_enable, 1428c2ecf20Sopenharmony_ci .disable = rb070d30_panel_disable, 1438c2ecf20Sopenharmony_ci .unprepare = rb070d30_panel_unprepare, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (!ctx) 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd"); 1568c2ecf20Sopenharmony_ci if (IS_ERR(ctx->supply)) 1578c2ecf20Sopenharmony_ci return PTR_ERR(ctx->supply); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 1608c2ecf20Sopenharmony_ci ctx->dsi = dsi; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci drm_panel_init(&ctx->panel, &dsi->dev, &rb070d30_panel_funcs, 1638c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); 1668c2ecf20Sopenharmony_ci if (IS_ERR(ctx->gpios.reset)) { 1678c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Couldn't get our reset GPIO\n"); 1688c2ecf20Sopenharmony_ci return PTR_ERR(ctx->gpios.reset); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ctx->gpios.power = devm_gpiod_get(&dsi->dev, "power", GPIOD_OUT_LOW); 1728c2ecf20Sopenharmony_ci if (IS_ERR(ctx->gpios.power)) { 1738c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Couldn't get our power GPIO\n"); 1748c2ecf20Sopenharmony_ci return PTR_ERR(ctx->gpios.power); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * We don't change the state of that GPIO later on but we need 1798c2ecf20Sopenharmony_ci * to force it into a low state. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci ctx->gpios.updn = devm_gpiod_get(&dsi->dev, "updn", GPIOD_OUT_LOW); 1828c2ecf20Sopenharmony_ci if (IS_ERR(ctx->gpios.updn)) { 1838c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Couldn't get our updn GPIO\n"); 1848c2ecf20Sopenharmony_ci return PTR_ERR(ctx->gpios.updn); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * We don't change the state of that GPIO later on but we need 1898c2ecf20Sopenharmony_ci * to force it into a low state. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci ctx->gpios.shlr = devm_gpiod_get(&dsi->dev, "shlr", GPIOD_OUT_LOW); 1928c2ecf20Sopenharmony_ci if (IS_ERR(ctx->gpios.shlr)) { 1938c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Couldn't get our shlr GPIO\n"); 1948c2ecf20Sopenharmony_ci return PTR_ERR(ctx->gpios.shlr); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = drm_panel_of_backlight(&ctx->panel); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci drm_panel_add(&ctx->panel); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM; 2048c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 2058c2ecf20Sopenharmony_ci dsi->lanes = 4; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return mipi_dsi_attach(dsi); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci mipi_dsi_detach(dsi); 2158c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct of_device_id rb070d30_panel_of_match[] = { 2218c2ecf20Sopenharmony_ci { .compatible = "ronbo,rb070d30" }, 2228c2ecf20Sopenharmony_ci { /* sentinel */ }, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rb070d30_panel_of_match); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver rb070d30_panel_driver = { 2278c2ecf20Sopenharmony_ci .probe = rb070d30_panel_dsi_probe, 2288c2ecf20Sopenharmony_ci .remove = rb070d30_panel_dsi_remove, 2298c2ecf20Sopenharmony_ci .driver = { 2308c2ecf20Sopenharmony_ci .name = "panel-ronbo-rb070d30", 2318c2ecf20Sopenharmony_ci .of_match_table = rb070d30_panel_of_match, 2328c2ecf20Sopenharmony_ci }, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(rb070d30_panel_driver); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>"); 2378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Konstantin Sudakov <k.sudakov@integrasources.com>"); 2388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ronbo RB070D30 Panel Driver"); 2398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 240