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