1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2015 Red Hat 4 * Copyright (C) 2015 Sony Mobile Communications Inc. 5 * Author: Werner Johansson <werner.johansson@sonymobile.com> 6 * 7 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com> 8 */ 9 10 #include <linux/delay.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/regulator/consumer.h> 14 15 #include <video/mipi_display.h> 16 17 #include <drm/drm_crtc.h> 18 #include <drm/drm_device.h> 19 #include <drm/drm_mipi_dsi.h> 20 #include <drm/drm_panel.h> 21 22 /* 23 * When power is turned off to this panel a minimum off time of 500ms has to be 24 * observed before powering back on as there's no external reset pin. Keep 25 * track of earliest wakeup time and delay subsequent prepare call accordingly 26 */ 27 #define MIN_POFF_MS (500) 28 29 struct wuxga_nt_panel { 30 struct drm_panel base; 31 struct mipi_dsi_device *dsi; 32 33 struct regulator *supply; 34 35 ktime_t earliest_wake; 36 37 const struct drm_display_mode *mode; 38 }; 39 40 static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel) 41 { 42 return container_of(panel, struct wuxga_nt_panel, base); 43 } 44 45 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt) 46 { 47 return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi); 48 } 49 50 static int wuxga_nt_panel_disable(struct drm_panel *panel) 51 { 52 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 53 54 return mipi_dsi_shutdown_peripheral(wuxga_nt->dsi); 55 } 56 57 static int wuxga_nt_panel_unprepare(struct drm_panel *panel) 58 { 59 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 60 61 regulator_disable(wuxga_nt->supply); 62 wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS); 63 64 return 0; 65 } 66 67 static int wuxga_nt_panel_prepare(struct drm_panel *panel) 68 { 69 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 70 int ret; 71 s64 enablewait; 72 73 /* 74 * If the user re-enabled the panel before the required off-time then 75 * we need to wait the remaining period before re-enabling regulator 76 */ 77 enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real()); 78 79 /* Sanity check, this should never happen */ 80 if (enablewait > MIN_POFF_MS) 81 enablewait = MIN_POFF_MS; 82 83 if (enablewait > 0) 84 msleep(enablewait); 85 86 ret = regulator_enable(wuxga_nt->supply); 87 if (ret < 0) 88 return ret; 89 90 /* 91 * A minimum delay of 250ms is required after power-up until commands 92 * can be sent 93 */ 94 msleep(250); 95 96 ret = wuxga_nt_panel_on(wuxga_nt); 97 if (ret < 0) { 98 dev_err(panel->dev, "failed to set panel on: %d\n", ret); 99 goto poweroff; 100 } 101 102 return 0; 103 104 poweroff: 105 regulator_disable(wuxga_nt->supply); 106 107 return ret; 108 } 109 110 static const struct drm_display_mode default_mode = { 111 .clock = 164402, 112 .hdisplay = 1920, 113 .hsync_start = 1920 + 152, 114 .hsync_end = 1920 + 152 + 52, 115 .htotal = 1920 + 152 + 52 + 20, 116 .vdisplay = 1200, 117 .vsync_start = 1200 + 24, 118 .vsync_end = 1200 + 24 + 6, 119 .vtotal = 1200 + 24 + 6 + 48, 120 }; 121 122 static int wuxga_nt_panel_get_modes(struct drm_panel *panel, 123 struct drm_connector *connector) 124 { 125 struct drm_display_mode *mode; 126 127 mode = drm_mode_duplicate(connector->dev, &default_mode); 128 if (!mode) { 129 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 130 default_mode.hdisplay, default_mode.vdisplay, 131 drm_mode_vrefresh(&default_mode)); 132 return -ENOMEM; 133 } 134 135 drm_mode_set_name(mode); 136 137 drm_mode_probed_add(connector, mode); 138 139 connector->display_info.width_mm = 217; 140 connector->display_info.height_mm = 136; 141 142 return 1; 143 } 144 145 static const struct drm_panel_funcs wuxga_nt_panel_funcs = { 146 .disable = wuxga_nt_panel_disable, 147 .unprepare = wuxga_nt_panel_unprepare, 148 .prepare = wuxga_nt_panel_prepare, 149 .get_modes = wuxga_nt_panel_get_modes, 150 }; 151 152 static const struct of_device_id wuxga_nt_of_match[] = { 153 { .compatible = "panasonic,vvx10f034n00", }, 154 { } 155 }; 156 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match); 157 158 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt) 159 { 160 struct device *dev = &wuxga_nt->dsi->dev; 161 int ret; 162 163 wuxga_nt->mode = &default_mode; 164 165 wuxga_nt->supply = devm_regulator_get(dev, "power"); 166 if (IS_ERR(wuxga_nt->supply)) 167 return PTR_ERR(wuxga_nt->supply); 168 169 drm_panel_init(&wuxga_nt->base, &wuxga_nt->dsi->dev, 170 &wuxga_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI); 171 172 ret = drm_panel_of_backlight(&wuxga_nt->base); 173 if (ret) 174 return ret; 175 176 drm_panel_add(&wuxga_nt->base); 177 178 return 0; 179 } 180 181 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt) 182 { 183 if (wuxga_nt->base.dev) 184 drm_panel_remove(&wuxga_nt->base); 185 } 186 187 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi) 188 { 189 struct wuxga_nt_panel *wuxga_nt; 190 int ret; 191 192 dsi->lanes = 4; 193 dsi->format = MIPI_DSI_FMT_RGB888; 194 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 195 MIPI_DSI_MODE_VIDEO_HSE | 196 MIPI_DSI_CLOCK_NON_CONTINUOUS | 197 MIPI_DSI_MODE_LPM; 198 199 wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL); 200 if (!wuxga_nt) 201 return -ENOMEM; 202 203 mipi_dsi_set_drvdata(dsi, wuxga_nt); 204 205 wuxga_nt->dsi = dsi; 206 207 ret = wuxga_nt_panel_add(wuxga_nt); 208 if (ret < 0) 209 return ret; 210 211 ret = mipi_dsi_attach(dsi); 212 if (ret < 0) { 213 wuxga_nt_panel_del(wuxga_nt); 214 return ret; 215 } 216 217 return 0; 218 } 219 220 static void wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) 221 { 222 struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); 223 int ret; 224 225 ret = mipi_dsi_detach(dsi); 226 if (ret < 0) 227 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 228 229 wuxga_nt_panel_del(wuxga_nt); 230 } 231 232 static struct mipi_dsi_driver wuxga_nt_panel_driver = { 233 .driver = { 234 .name = "panel-panasonic-vvx10f034n00", 235 .of_match_table = wuxga_nt_of_match, 236 }, 237 .probe = wuxga_nt_panel_probe, 238 .remove = wuxga_nt_panel_remove, 239 }; 240 module_mipi_dsi_driver(wuxga_nt_panel_driver); 241 242 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>"); 243 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver"); 244 MODULE_LICENSE("GPL v2"); 245