1 /* 2 * Copyright (C) 2015 Red Hat 3 * Copyright (C) 2015 Sony Mobile Communications Inc. 4 * Author: Werner Johansson <werner.johansson@sonymobile.com> 5 * 6 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published by 10 * the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include <linux/backlight.h> 22 #include <linux/module.h> 23 #include <linux/of.h> 24 #include <linux/regulator/consumer.h> 25 26 #include <drm/drmP.h> 27 #include <drm/drm_crtc.h> 28 #include <drm/drm_mipi_dsi.h> 29 #include <drm/drm_panel.h> 30 31 #include <video/mipi_display.h> 32 33 /* 34 * When power is turned off to this panel a minimum off time of 500ms has to be 35 * observed before powering back on as there's no external reset pin. Keep 36 * track of earliest wakeup time and delay subsequent prepare call accordingly 37 */ 38 #define MIN_POFF_MS (500) 39 40 struct wuxga_nt_panel { 41 struct drm_panel base; 42 struct mipi_dsi_device *dsi; 43 44 struct backlight_device *backlight; 45 struct regulator *supply; 46 47 bool prepared; 48 bool enabled; 49 50 ktime_t earliest_wake; 51 52 const struct drm_display_mode *mode; 53 }; 54 55 static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel) 56 { 57 return container_of(panel, struct wuxga_nt_panel, base); 58 } 59 60 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt) 61 { 62 struct mipi_dsi_device *dsi = wuxga_nt->dsi; 63 int ret; 64 65 ret = mipi_dsi_turn_on_peripheral(dsi); 66 if (ret < 0) 67 return ret; 68 69 return 0; 70 } 71 72 static int wuxga_nt_panel_disable(struct drm_panel *panel) 73 { 74 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 75 76 if (!wuxga_nt->enabled) 77 return 0; 78 79 mipi_dsi_shutdown_peripheral(wuxga_nt->dsi); 80 81 if (wuxga_nt->backlight) { 82 wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN; 83 wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK; 84 backlight_update_status(wuxga_nt->backlight); 85 } 86 87 wuxga_nt->enabled = false; 88 89 return 0; 90 } 91 92 static int wuxga_nt_panel_unprepare(struct drm_panel *panel) 93 { 94 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 95 96 if (!wuxga_nt->prepared) 97 return 0; 98 99 regulator_disable(wuxga_nt->supply); 100 wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS); 101 wuxga_nt->prepared = false; 102 103 return 0; 104 } 105 106 static int wuxga_nt_panel_prepare(struct drm_panel *panel) 107 { 108 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 109 int ret; 110 s64 enablewait; 111 112 if (wuxga_nt->prepared) 113 return 0; 114 115 /* 116 * If the user re-enabled the panel before the required off-time then 117 * we need to wait the remaining period before re-enabling regulator 118 */ 119 enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real()); 120 121 /* Sanity check, this should never happen */ 122 if (enablewait > MIN_POFF_MS) 123 enablewait = MIN_POFF_MS; 124 125 if (enablewait > 0) 126 msleep(enablewait); 127 128 ret = regulator_enable(wuxga_nt->supply); 129 if (ret < 0) 130 return ret; 131 132 /* 133 * A minimum delay of 250ms is required after power-up until commands 134 * can be sent 135 */ 136 msleep(250); 137 138 ret = wuxga_nt_panel_on(wuxga_nt); 139 if (ret < 0) { 140 dev_err(panel->dev, "failed to set panel on: %d\n", ret); 141 goto poweroff; 142 } 143 144 wuxga_nt->prepared = true; 145 146 return 0; 147 148 poweroff: 149 regulator_disable(wuxga_nt->supply); 150 151 return ret; 152 } 153 154 static int wuxga_nt_panel_enable(struct drm_panel *panel) 155 { 156 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); 157 158 if (wuxga_nt->enabled) 159 return 0; 160 161 if (wuxga_nt->backlight) { 162 wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK; 163 wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK; 164 backlight_update_status(wuxga_nt->backlight); 165 } 166 167 wuxga_nt->enabled = true; 168 169 return 0; 170 } 171 172 static const struct drm_display_mode default_mode = { 173 .clock = 164402, 174 .hdisplay = 1920, 175 .hsync_start = 1920 + 152, 176 .hsync_end = 1920 + 152 + 52, 177 .htotal = 1920 + 152 + 52 + 20, 178 .vdisplay = 1200, 179 .vsync_start = 1200 + 24, 180 .vsync_end = 1200 + 24 + 6, 181 .vtotal = 1200 + 24 + 6 + 48, 182 .vrefresh = 60, 183 }; 184 185 static int wuxga_nt_panel_get_modes(struct drm_panel *panel) 186 { 187 struct drm_display_mode *mode; 188 189 mode = drm_mode_duplicate(panel->drm, &default_mode); 190 if (!mode) { 191 dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n", 192 default_mode.hdisplay, default_mode.vdisplay, 193 default_mode.vrefresh); 194 return -ENOMEM; 195 } 196 197 drm_mode_set_name(mode); 198 199 drm_mode_probed_add(panel->connector, mode); 200 201 panel->connector->display_info.width_mm = 217; 202 panel->connector->display_info.height_mm = 136; 203 204 return 1; 205 } 206 207 static const struct drm_panel_funcs wuxga_nt_panel_funcs = { 208 .disable = wuxga_nt_panel_disable, 209 .unprepare = wuxga_nt_panel_unprepare, 210 .prepare = wuxga_nt_panel_prepare, 211 .enable = wuxga_nt_panel_enable, 212 .get_modes = wuxga_nt_panel_get_modes, 213 }; 214 215 static const struct of_device_id wuxga_nt_of_match[] = { 216 { .compatible = "panasonic,vvx10f034n00", }, 217 { } 218 }; 219 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match); 220 221 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt) 222 { 223 struct device *dev = &wuxga_nt->dsi->dev; 224 struct device_node *np; 225 int ret; 226 227 wuxga_nt->mode = &default_mode; 228 229 wuxga_nt->supply = devm_regulator_get(dev, "power"); 230 if (IS_ERR(wuxga_nt->supply)) 231 return PTR_ERR(wuxga_nt->supply); 232 233 np = of_parse_phandle(dev->of_node, "backlight", 0); 234 if (np) { 235 wuxga_nt->backlight = of_find_backlight_by_node(np); 236 of_node_put(np); 237 238 if (!wuxga_nt->backlight) 239 return -EPROBE_DEFER; 240 } 241 242 drm_panel_init(&wuxga_nt->base); 243 wuxga_nt->base.funcs = &wuxga_nt_panel_funcs; 244 wuxga_nt->base.dev = &wuxga_nt->dsi->dev; 245 246 ret = drm_panel_add(&wuxga_nt->base); 247 if (ret < 0) 248 goto put_backlight; 249 250 return 0; 251 252 put_backlight: 253 if (wuxga_nt->backlight) 254 put_device(&wuxga_nt->backlight->dev); 255 256 return ret; 257 } 258 259 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt) 260 { 261 if (wuxga_nt->base.dev) 262 drm_panel_remove(&wuxga_nt->base); 263 264 if (wuxga_nt->backlight) 265 put_device(&wuxga_nt->backlight->dev); 266 } 267 268 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi) 269 { 270 struct wuxga_nt_panel *wuxga_nt; 271 int ret; 272 273 dsi->lanes = 4; 274 dsi->format = MIPI_DSI_FMT_RGB888; 275 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 276 MIPI_DSI_MODE_VIDEO_HSE | 277 MIPI_DSI_CLOCK_NON_CONTINUOUS | 278 MIPI_DSI_MODE_LPM; 279 280 wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL); 281 if (!wuxga_nt) 282 return -ENOMEM; 283 284 mipi_dsi_set_drvdata(dsi, wuxga_nt); 285 286 wuxga_nt->dsi = dsi; 287 288 ret = wuxga_nt_panel_add(wuxga_nt); 289 if (ret < 0) 290 return ret; 291 292 return mipi_dsi_attach(dsi); 293 } 294 295 static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) 296 { 297 struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); 298 int ret; 299 300 ret = wuxga_nt_panel_disable(&wuxga_nt->base); 301 if (ret < 0) 302 dev_err(&dsi->dev, "failed to disable panel: %d\n", ret); 303 304 ret = mipi_dsi_detach(dsi); 305 if (ret < 0) 306 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 307 308 drm_panel_detach(&wuxga_nt->base); 309 wuxga_nt_panel_del(wuxga_nt); 310 311 return 0; 312 } 313 314 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi) 315 { 316 struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); 317 318 wuxga_nt_panel_disable(&wuxga_nt->base); 319 } 320 321 static struct mipi_dsi_driver wuxga_nt_panel_driver = { 322 .driver = { 323 .name = "panel-panasonic-vvx10f034n00", 324 .of_match_table = wuxga_nt_of_match, 325 }, 326 .probe = wuxga_nt_panel_probe, 327 .remove = wuxga_nt_panel_remove, 328 .shutdown = wuxga_nt_panel_shutdown, 329 }; 330 module_mipi_dsi_driver(wuxga_nt_panel_driver); 331 332 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>"); 333 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver"); 334 MODULE_LICENSE("GPL v2"); 335