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