245 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015 Red Hat4 * Copyright (C) 2015 Sony Mobile Communications Inc.5 * Author: Werner Johansson <werner.johansson@sonymobile.com>6 *7 * Based on AUO panel driver by Rob Clark <robdclark@gmail.com>8 */9 10#include <linux/delay.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/regulator/consumer.h>14 15#include <video/mipi_display.h>16 17#include <drm/drm_crtc.h>18#include <drm/drm_device.h>19#include <drm/drm_mipi_dsi.h>20#include <drm/drm_panel.h>21 22/*23 * When power is turned off to this panel a minimum off time of 500ms has to be24 * observed before powering back on as there's no external reset pin. Keep25 * track of earliest wakeup time and delay subsequent prepare call accordingly26 */27#define MIN_POFF_MS (500)28 29struct wuxga_nt_panel {30 struct drm_panel base;31 struct mipi_dsi_device *dsi;32 33 struct regulator *supply;34 35 ktime_t earliest_wake;36 37 const struct drm_display_mode *mode;38};39 40static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)41{42 return container_of(panel, struct wuxga_nt_panel, base);43}44 45static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)46{47 return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);48}49 50static int wuxga_nt_panel_disable(struct drm_panel *panel)51{52 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);53 54 return mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);55}56 57static int wuxga_nt_panel_unprepare(struct drm_panel *panel)58{59 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);60 61 regulator_disable(wuxga_nt->supply);62 wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS);63 64 return 0;65}66 67static int wuxga_nt_panel_prepare(struct drm_panel *panel)68{69 struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);70 int ret;71 s64 enablewait;72 73 /*74 * If the user re-enabled the panel before the required off-time then75 * we need to wait the remaining period before re-enabling regulator76 */77 enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real());78 79 /* Sanity check, this should never happen */80 if (enablewait > MIN_POFF_MS)81 enablewait = MIN_POFF_MS;82 83 if (enablewait > 0)84 msleep(enablewait);85 86 ret = regulator_enable(wuxga_nt->supply);87 if (ret < 0)88 return ret;89 90 /*91 * A minimum delay of 250ms is required after power-up until commands92 * can be sent93 */94 msleep(250);95 96 ret = wuxga_nt_panel_on(wuxga_nt);97 if (ret < 0) {98 dev_err(panel->dev, "failed to set panel on: %d\n", ret);99 goto poweroff;100 }101 102 return 0;103 104poweroff:105 regulator_disable(wuxga_nt->supply);106 107 return ret;108}109 110static const struct drm_display_mode default_mode = {111 .clock = 164402,112 .hdisplay = 1920,113 .hsync_start = 1920 + 152,114 .hsync_end = 1920 + 152 + 52,115 .htotal = 1920 + 152 + 52 + 20,116 .vdisplay = 1200,117 .vsync_start = 1200 + 24,118 .vsync_end = 1200 + 24 + 6,119 .vtotal = 1200 + 24 + 6 + 48,120};121 122static int wuxga_nt_panel_get_modes(struct drm_panel *panel,123 struct drm_connector *connector)124{125 struct drm_display_mode *mode;126 127 mode = drm_mode_duplicate(connector->dev, &default_mode);128 if (!mode) {129 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",130 default_mode.hdisplay, default_mode.vdisplay,131 drm_mode_vrefresh(&default_mode));132 return -ENOMEM;133 }134 135 drm_mode_set_name(mode);136 137 drm_mode_probed_add(connector, mode);138 139 connector->display_info.width_mm = 217;140 connector->display_info.height_mm = 136;141 142 return 1;143}144 145static const struct drm_panel_funcs wuxga_nt_panel_funcs = {146 .disable = wuxga_nt_panel_disable,147 .unprepare = wuxga_nt_panel_unprepare,148 .prepare = wuxga_nt_panel_prepare,149 .get_modes = wuxga_nt_panel_get_modes,150};151 152static const struct of_device_id wuxga_nt_of_match[] = {153 { .compatible = "panasonic,vvx10f034n00", },154 { }155};156MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);157 158static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)159{160 struct device *dev = &wuxga_nt->dsi->dev;161 int ret;162 163 wuxga_nt->mode = &default_mode;164 165 wuxga_nt->supply = devm_regulator_get(dev, "power");166 if (IS_ERR(wuxga_nt->supply))167 return PTR_ERR(wuxga_nt->supply);168 169 drm_panel_init(&wuxga_nt->base, &wuxga_nt->dsi->dev,170 &wuxga_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);171 172 ret = drm_panel_of_backlight(&wuxga_nt->base);173 if (ret)174 return ret;175 176 drm_panel_add(&wuxga_nt->base);177 178 return 0;179}180 181static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)182{183 if (wuxga_nt->base.dev)184 drm_panel_remove(&wuxga_nt->base);185}186 187static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)188{189 struct wuxga_nt_panel *wuxga_nt;190 int ret;191 192 dsi->lanes = 4;193 dsi->format = MIPI_DSI_FMT_RGB888;194 dsi->mode_flags = MIPI_DSI_MODE_VIDEO |195 MIPI_DSI_MODE_VIDEO_HSE |196 MIPI_DSI_CLOCK_NON_CONTINUOUS |197 MIPI_DSI_MODE_LPM;198 199 wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL);200 if (!wuxga_nt)201 return -ENOMEM;202 203 mipi_dsi_set_drvdata(dsi, wuxga_nt);204 205 wuxga_nt->dsi = dsi;206 207 ret = wuxga_nt_panel_add(wuxga_nt);208 if (ret < 0)209 return ret;210 211 ret = mipi_dsi_attach(dsi);212 if (ret < 0) {213 wuxga_nt_panel_del(wuxga_nt);214 return ret;215 }216 217 return 0;218}219 220static void wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)221{222 struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);223 int ret;224 225 ret = mipi_dsi_detach(dsi);226 if (ret < 0)227 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);228 229 wuxga_nt_panel_del(wuxga_nt);230}231 232static struct mipi_dsi_driver wuxga_nt_panel_driver = {233 .driver = {234 .name = "panel-panasonic-vvx10f034n00",235 .of_match_table = wuxga_nt_of_match,236 },237 .probe = wuxga_nt_panel_probe,238 .remove = wuxga_nt_panel_remove,239};240module_mipi_dsi_driver(wuxga_nt_panel_driver);241 242MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");243MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");244MODULE_LICENSE("GPL v2");245