Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

Need help to get security updates for your Linux BSP?
Loading...
Note: File does not exist in v3.1.
  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	return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
 63}
 64
 65static 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
 86static 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
100static 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
142poweroff:
143	regulator_disable(wuxga_nt->supply);
144
145	return ret;
146}
147
148static 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
166static 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
179static 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
201static 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
209static const struct of_device_id wuxga_nt_of_match[] = {
210	{ .compatible = "panasonic,vvx10f034n00", },
211	{ }
212};
213MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
214
215static 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
246put_backlight:
247	if (wuxga_nt->backlight)
248		put_device(&wuxga_nt->backlight->dev);
249
250	return ret;
251}
252
253static 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
262static 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
289static 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	drm_panel_detach(&wuxga_nt->base);
303	wuxga_nt_panel_del(wuxga_nt);
304
305	return 0;
306}
307
308static 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
315static 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};
324module_mipi_dsi_driver(wuxga_nt_panel_driver);
325
326MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>");
327MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver");
328MODULE_LICENSE("GPL v2");