linux/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
<<
>>
Prefs
   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
  40struct 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
  55static 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
  60static 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
  72static 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
  92static 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
 106static 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
 148poweroff:
 149        regulator_disable(wuxga_nt->supply);
 150
 151        return ret;
 152}
 153
 154static 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
 172static 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
 185static 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
 207static 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
 215static const struct of_device_id wuxga_nt_of_match[] = {
 216        { .compatible = "panasonic,vvx10f034n00", },
 217        { }
 218};
 219MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
 220
 221static 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
 252put_backlight:
 253        if (wuxga_nt->backlight)
 254                put_device(&wuxga_nt->backlight->dev);
 255
 256        return ret;
 257}
 258
 259static 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
 268static 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
 295static 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
 314static 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
 321static 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};
 330module_mipi_dsi_driver(wuxga_nt_panel_driver);
 331
 332MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
 333MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
 334MODULE_LICENSE("GPL v2");
 335