Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2015 Red Hat
0004  * Copyright (C) 2015 Sony Mobile Communications Inc.
0005  * Author: Werner Johansson <werner.johansson@sonymobile.com>
0006  *
0007  * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
0008  */
0009 
0010 #include <linux/delay.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/regulator/consumer.h>
0014 
0015 #include <video/mipi_display.h>
0016 
0017 #include <drm/drm_crtc.h>
0018 #include <drm/drm_device.h>
0019 #include <drm/drm_mipi_dsi.h>
0020 #include <drm/drm_panel.h>
0021 
0022 /*
0023  * When power is turned off to this panel a minimum off time of 500ms has to be
0024  * observed before powering back on as there's no external reset pin. Keep
0025  * track of earliest wakeup time and delay subsequent prepare call accordingly
0026  */
0027 #define MIN_POFF_MS (500)
0028 
0029 struct wuxga_nt_panel {
0030     struct drm_panel base;
0031     struct mipi_dsi_device *dsi;
0032 
0033     struct regulator *supply;
0034 
0035     bool prepared;
0036     bool enabled;
0037 
0038     ktime_t earliest_wake;
0039 
0040     const struct drm_display_mode *mode;
0041 };
0042 
0043 static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
0044 {
0045     return container_of(panel, struct wuxga_nt_panel, base);
0046 }
0047 
0048 static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
0049 {
0050     return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
0051 }
0052 
0053 static int wuxga_nt_panel_disable(struct drm_panel *panel)
0054 {
0055     struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
0056     int mipi_ret, bl_ret = 0;
0057 
0058     if (!wuxga_nt->enabled)
0059         return 0;
0060 
0061     mipi_ret = mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
0062 
0063     wuxga_nt->enabled = false;
0064 
0065     return mipi_ret ? mipi_ret : bl_ret;
0066 }
0067 
0068 static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
0069 {
0070     struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
0071 
0072     if (!wuxga_nt->prepared)
0073         return 0;
0074 
0075     regulator_disable(wuxga_nt->supply);
0076     wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS);
0077     wuxga_nt->prepared = false;
0078 
0079     return 0;
0080 }
0081 
0082 static int wuxga_nt_panel_prepare(struct drm_panel *panel)
0083 {
0084     struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
0085     int ret;
0086     s64 enablewait;
0087 
0088     if (wuxga_nt->prepared)
0089         return 0;
0090 
0091     /*
0092      * If the user re-enabled the panel before the required off-time then
0093      * we need to wait the remaining period before re-enabling regulator
0094      */
0095     enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());
0096 
0097     /* Sanity check, this should never happen */
0098     if (enablewait > MIN_POFF_MS)
0099         enablewait = MIN_POFF_MS;
0100 
0101     if (enablewait > 0)
0102         msleep(enablewait);
0103 
0104     ret = regulator_enable(wuxga_nt->supply);
0105     if (ret < 0)
0106         return ret;
0107 
0108     /*
0109      * A minimum delay of 250ms is required after power-up until commands
0110      * can be sent
0111      */
0112     msleep(250);
0113 
0114     ret = wuxga_nt_panel_on(wuxga_nt);
0115     if (ret < 0) {
0116         dev_err(panel->dev, "failed to set panel on: %d\n", ret);
0117         goto poweroff;
0118     }
0119 
0120     wuxga_nt->prepared = true;
0121 
0122     return 0;
0123 
0124 poweroff:
0125     regulator_disable(wuxga_nt->supply);
0126 
0127     return ret;
0128 }
0129 
0130 static int wuxga_nt_panel_enable(struct drm_panel *panel)
0131 {
0132     struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
0133 
0134     if (wuxga_nt->enabled)
0135         return 0;
0136 
0137     wuxga_nt->enabled = true;
0138 
0139     return 0;
0140 }
0141 
0142 static const struct drm_display_mode default_mode = {
0143     .clock = 164402,
0144     .hdisplay = 1920,
0145     .hsync_start = 1920 + 152,
0146     .hsync_end = 1920 + 152 + 52,
0147     .htotal = 1920 + 152 + 52 + 20,
0148     .vdisplay = 1200,
0149     .vsync_start = 1200 + 24,
0150     .vsync_end = 1200 + 24 + 6,
0151     .vtotal = 1200 + 24 + 6 + 48,
0152 };
0153 
0154 static int wuxga_nt_panel_get_modes(struct drm_panel *panel,
0155                     struct drm_connector *connector)
0156 {
0157     struct drm_display_mode *mode;
0158 
0159     mode = drm_mode_duplicate(connector->dev, &default_mode);
0160     if (!mode) {
0161         dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
0162             default_mode.hdisplay, default_mode.vdisplay,
0163             drm_mode_vrefresh(&default_mode));
0164         return -ENOMEM;
0165     }
0166 
0167     drm_mode_set_name(mode);
0168 
0169     drm_mode_probed_add(connector, mode);
0170 
0171     connector->display_info.width_mm = 217;
0172     connector->display_info.height_mm = 136;
0173 
0174     return 1;
0175 }
0176 
0177 static const struct drm_panel_funcs wuxga_nt_panel_funcs = {
0178     .disable = wuxga_nt_panel_disable,
0179     .unprepare = wuxga_nt_panel_unprepare,
0180     .prepare = wuxga_nt_panel_prepare,
0181     .enable = wuxga_nt_panel_enable,
0182     .get_modes = wuxga_nt_panel_get_modes,
0183 };
0184 
0185 static const struct of_device_id wuxga_nt_of_match[] = {
0186     { .compatible = "panasonic,vvx10f034n00", },
0187     { }
0188 };
0189 MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
0190 
0191 static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
0192 {
0193     struct device *dev = &wuxga_nt->dsi->dev;
0194     int ret;
0195 
0196     wuxga_nt->mode = &default_mode;
0197 
0198     wuxga_nt->supply = devm_regulator_get(dev, "power");
0199     if (IS_ERR(wuxga_nt->supply))
0200         return PTR_ERR(wuxga_nt->supply);
0201 
0202     drm_panel_init(&wuxga_nt->base, &wuxga_nt->dsi->dev,
0203                &wuxga_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
0204 
0205     ret = drm_panel_of_backlight(&wuxga_nt->base);
0206     if (ret)
0207         return ret;
0208 
0209     drm_panel_add(&wuxga_nt->base);
0210 
0211     return 0;
0212 }
0213 
0214 static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
0215 {
0216     if (wuxga_nt->base.dev)
0217         drm_panel_remove(&wuxga_nt->base);
0218 }
0219 
0220 static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
0221 {
0222     struct wuxga_nt_panel *wuxga_nt;
0223     int ret;
0224 
0225     dsi->lanes = 4;
0226     dsi->format = MIPI_DSI_FMT_RGB888;
0227     dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
0228             MIPI_DSI_MODE_VIDEO_HSE |
0229             MIPI_DSI_CLOCK_NON_CONTINUOUS |
0230             MIPI_DSI_MODE_LPM;
0231 
0232     wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);
0233     if (!wuxga_nt)
0234         return -ENOMEM;
0235 
0236     mipi_dsi_set_drvdata(dsi, wuxga_nt);
0237 
0238     wuxga_nt->dsi = dsi;
0239 
0240     ret = wuxga_nt_panel_add(wuxga_nt);
0241     if (ret < 0)
0242         return ret;
0243 
0244     ret = mipi_dsi_attach(dsi);
0245     if (ret < 0) {
0246         wuxga_nt_panel_del(wuxga_nt);
0247         return ret;
0248     }
0249 
0250     return 0;
0251 }
0252 
0253 static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
0254 {
0255     struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
0256     int ret;
0257 
0258     ret = drm_panel_disable(&wuxga_nt->base);
0259     if (ret < 0)
0260         dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
0261 
0262     ret = mipi_dsi_detach(dsi);
0263     if (ret < 0)
0264         dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
0265 
0266     wuxga_nt_panel_del(wuxga_nt);
0267 
0268     return 0;
0269 }
0270 
0271 static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
0272 {
0273     struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
0274 
0275     drm_panel_disable(&wuxga_nt->base);
0276 }
0277 
0278 static struct mipi_dsi_driver wuxga_nt_panel_driver = {
0279     .driver = {
0280         .name = "panel-panasonic-vvx10f034n00",
0281         .of_match_table = wuxga_nt_of_match,
0282     },
0283     .probe = wuxga_nt_panel_probe,
0284     .remove = wuxga_nt_panel_remove,
0285     .shutdown = wuxga_nt_panel_shutdown,
0286 };
0287 module_mipi_dsi_driver(wuxga_nt_panel_driver);
0288 
0289 MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
0290 MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
0291 MODULE_LICENSE("GPL v2");