1994ea402SLinus Walleij // SPDX-License-Identifier: GPL-2.0+
2994ea402SLinus Walleij /*
3994ea402SLinus Walleij  * MIPI-DSI Novatek NT35560-based panel controller.
4994ea402SLinus Walleij  *
5994ea402SLinus Walleij  * Supported panels include:
6994ea402SLinus Walleij  * Sony ACX424AKM - a 480x854 AMOLED DSI panel
7994ea402SLinus Walleij  * Sony ACX424AKP - a 480x864 AMOLED DSI panel
8994ea402SLinus Walleij  *
9994ea402SLinus Walleij  * Copyright (C) Linaro Ltd. 2019-2021
10994ea402SLinus Walleij  * Author: Linus Walleij
11994ea402SLinus Walleij  * Based on code and know-how from Marcus Lorentzon
12994ea402SLinus Walleij  * Copyright (C) ST-Ericsson SA 2010
13de45f0a3SLinus Walleij  * Based on code and know-how from Johan Olson and Joakim Wesslen
14de45f0a3SLinus Walleij  * Copyright (C) Sony Ericsson Mobile Communications 2010
15994ea402SLinus Walleij  */
16994ea402SLinus Walleij #include <linux/backlight.h>
17994ea402SLinus Walleij #include <linux/delay.h>
18994ea402SLinus Walleij #include <linux/gpio/consumer.h>
19994ea402SLinus Walleij #include <linux/module.h>
20994ea402SLinus Walleij #include <linux/of.h>
21994ea402SLinus Walleij #include <linux/regulator/consumer.h>
22994ea402SLinus Walleij 
23994ea402SLinus Walleij #include <video/mipi_display.h>
24994ea402SLinus Walleij 
25994ea402SLinus Walleij #include <drm/drm_mipi_dsi.h>
26994ea402SLinus Walleij #include <drm/drm_modes.h>
27994ea402SLinus Walleij #include <drm/drm_panel.h>
28994ea402SLinus Walleij 
29994ea402SLinus Walleij #define NT35560_DCS_READ_ID1		0xDA
30994ea402SLinus Walleij #define NT35560_DCS_READ_ID2		0xDB
31994ea402SLinus Walleij #define NT35560_DCS_READ_ID3		0xDC
32994ea402SLinus Walleij #define NT35560_DCS_SET_MDDI		0xAE
33994ea402SLinus Walleij 
34994ea402SLinus Walleij /*
35994ea402SLinus Walleij  * Sony seems to use vendor ID 0x81
36994ea402SLinus Walleij  */
37e78089daSLinus Walleij #define DISPLAY_SONY_ACX424AKP_ID1	0x8103
38994ea402SLinus Walleij #define DISPLAY_SONY_ACX424AKP_ID2	0x811a
39e78089daSLinus Walleij #define DISPLAY_SONY_ACX424AKP_ID3	0x811b
40994ea402SLinus Walleij /*
41e78089daSLinus Walleij  * The fourth ID looks like a bug, vendor IDs begin at 0x80
42994ea402SLinus Walleij  * and panel 00 ... seems like default values.
43994ea402SLinus Walleij  */
44e78089daSLinus Walleij #define DISPLAY_SONY_ACX424AKP_ID4	0x8000
45994ea402SLinus Walleij 
46de45f0a3SLinus Walleij struct nt35560_config {
47de45f0a3SLinus Walleij 	const struct drm_display_mode *vid_mode;
48de45f0a3SLinus Walleij 	const struct drm_display_mode *cmd_mode;
49de45f0a3SLinus Walleij };
50de45f0a3SLinus Walleij 
51994ea402SLinus Walleij struct nt35560 {
52de45f0a3SLinus Walleij 	const struct nt35560_config *conf;
53994ea402SLinus Walleij 	struct drm_panel panel;
54994ea402SLinus Walleij 	struct device *dev;
55994ea402SLinus Walleij 	struct regulator *supply;
56994ea402SLinus Walleij 	struct gpio_desc *reset_gpio;
57994ea402SLinus Walleij 	bool video_mode;
58994ea402SLinus Walleij };
59994ea402SLinus Walleij 
60994ea402SLinus Walleij static const struct drm_display_mode sony_acx424akp_vid_mode = {
61994ea402SLinus Walleij 	.clock = 27234,
62994ea402SLinus Walleij 	.hdisplay = 480,
63994ea402SLinus Walleij 	.hsync_start = 480 + 15,
64994ea402SLinus Walleij 	.hsync_end = 480 + 15 + 0,
65994ea402SLinus Walleij 	.htotal = 480 + 15 + 0 + 15,
66994ea402SLinus Walleij 	.vdisplay = 864,
67994ea402SLinus Walleij 	.vsync_start = 864 + 14,
68994ea402SLinus Walleij 	.vsync_end = 864 + 14 + 1,
69994ea402SLinus Walleij 	.vtotal = 864 + 14 + 1 + 11,
70994ea402SLinus Walleij 	.width_mm = 48,
71994ea402SLinus Walleij 	.height_mm = 84,
72994ea402SLinus Walleij 	.flags = DRM_MODE_FLAG_PVSYNC,
73994ea402SLinus Walleij };
74994ea402SLinus Walleij 
75994ea402SLinus Walleij /*
76994ea402SLinus Walleij  * The timings are not very helpful as the display is used in
77994ea402SLinus Walleij  * command mode using the maximum HS frequency.
78994ea402SLinus Walleij  */
79994ea402SLinus Walleij static const struct drm_display_mode sony_acx424akp_cmd_mode = {
80994ea402SLinus Walleij 	.clock = 35478,
81994ea402SLinus Walleij 	.hdisplay = 480,
82994ea402SLinus Walleij 	.hsync_start = 480 + 154,
83994ea402SLinus Walleij 	.hsync_end = 480 + 154 + 16,
84994ea402SLinus Walleij 	.htotal = 480 + 154 + 16 + 32,
85994ea402SLinus Walleij 	.vdisplay = 864,
86994ea402SLinus Walleij 	.vsync_start = 864 + 1,
87994ea402SLinus Walleij 	.vsync_end = 864 + 1 + 1,
88994ea402SLinus Walleij 	.vtotal = 864 + 1 + 1 + 1,
89994ea402SLinus Walleij 	/*
90994ea402SLinus Walleij 	 * Some desired refresh rate, experiments at the maximum "pixel"
91994ea402SLinus Walleij 	 * clock speed (HS clock 420 MHz) yields around 117Hz.
92994ea402SLinus Walleij 	 */
93994ea402SLinus Walleij 	.width_mm = 48,
94994ea402SLinus Walleij 	.height_mm = 84,
95994ea402SLinus Walleij };
96994ea402SLinus Walleij 
97de45f0a3SLinus Walleij static const struct nt35560_config sony_acx424akp_data = {
98de45f0a3SLinus Walleij 	.vid_mode = &sony_acx424akp_vid_mode,
99de45f0a3SLinus Walleij 	.cmd_mode = &sony_acx424akp_cmd_mode,
100de45f0a3SLinus Walleij };
101de45f0a3SLinus Walleij 
102de45f0a3SLinus Walleij static const struct drm_display_mode sony_acx424akm_vid_mode = {
103de45f0a3SLinus Walleij 	.clock = 27234,
104de45f0a3SLinus Walleij 	.hdisplay = 480,
105de45f0a3SLinus Walleij 	.hsync_start = 480 + 15,
106de45f0a3SLinus Walleij 	.hsync_end = 480 + 15 + 0,
107de45f0a3SLinus Walleij 	.htotal = 480 + 15 + 0 + 15,
108de45f0a3SLinus Walleij 	.vdisplay = 854,
109de45f0a3SLinus Walleij 	.vsync_start = 854 + 14,
110de45f0a3SLinus Walleij 	.vsync_end = 854 + 14 + 1,
111de45f0a3SLinus Walleij 	.vtotal = 854 + 14 + 1 + 11,
112de45f0a3SLinus Walleij 	.width_mm = 46,
113de45f0a3SLinus Walleij 	.height_mm = 82,
114de45f0a3SLinus Walleij 	.flags = DRM_MODE_FLAG_PVSYNC,
115de45f0a3SLinus Walleij };
116de45f0a3SLinus Walleij 
117de45f0a3SLinus Walleij /*
118de45f0a3SLinus Walleij  * The timings are not very helpful as the display is used in
119de45f0a3SLinus Walleij  * command mode using the maximum HS frequency.
120de45f0a3SLinus Walleij  */
121de45f0a3SLinus Walleij static const struct drm_display_mode sony_acx424akm_cmd_mode = {
122de45f0a3SLinus Walleij 	.clock = 35478,
123de45f0a3SLinus Walleij 	.hdisplay = 480,
124de45f0a3SLinus Walleij 	.hsync_start = 480 + 154,
125de45f0a3SLinus Walleij 	.hsync_end = 480 + 154 + 16,
126de45f0a3SLinus Walleij 	.htotal = 480 + 154 + 16 + 32,
127de45f0a3SLinus Walleij 	.vdisplay = 854,
128de45f0a3SLinus Walleij 	.vsync_start = 854 + 1,
129de45f0a3SLinus Walleij 	.vsync_end = 854 + 1 + 1,
130de45f0a3SLinus Walleij 	.vtotal = 854 + 1 + 1 + 1,
131de45f0a3SLinus Walleij 	.width_mm = 46,
132de45f0a3SLinus Walleij 	.height_mm = 82,
133de45f0a3SLinus Walleij };
134de45f0a3SLinus Walleij 
135de45f0a3SLinus Walleij static const struct nt35560_config sony_acx424akm_data = {
136de45f0a3SLinus Walleij 	.vid_mode = &sony_acx424akm_vid_mode,
137de45f0a3SLinus Walleij 	.cmd_mode = &sony_acx424akm_cmd_mode,
138de45f0a3SLinus Walleij };
139de45f0a3SLinus Walleij 
panel_to_nt35560(struct drm_panel * panel)140994ea402SLinus Walleij static inline struct nt35560 *panel_to_nt35560(struct drm_panel *panel)
141994ea402SLinus Walleij {
142994ea402SLinus Walleij 	return container_of(panel, struct nt35560, panel);
143994ea402SLinus Walleij }
144994ea402SLinus Walleij 
145994ea402SLinus Walleij #define FOSC			20 /* 20Mhz */
146994ea402SLinus Walleij #define SCALE_FACTOR_NS_DIV_MHZ	1000
147994ea402SLinus Walleij 
nt35560_set_brightness(struct backlight_device * bl)148994ea402SLinus Walleij static int nt35560_set_brightness(struct backlight_device *bl)
149994ea402SLinus Walleij {
150994ea402SLinus Walleij 	struct nt35560 *nt = bl_get_data(bl);
151994ea402SLinus Walleij 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
152994ea402SLinus Walleij 	int period_ns = 1023;
153994ea402SLinus Walleij 	int duty_ns = bl->props.brightness;
154994ea402SLinus Walleij 	u8 pwm_ratio;
155994ea402SLinus Walleij 	u8 pwm_div;
156994ea402SLinus Walleij 	u8 par;
157994ea402SLinus Walleij 	int ret;
158994ea402SLinus Walleij 
159994ea402SLinus Walleij 	if (backlight_is_blank(bl)) {
160994ea402SLinus Walleij 		/* Disable backlight */
161994ea402SLinus Walleij 		par = 0x00;
162994ea402SLinus Walleij 		ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
163994ea402SLinus Walleij 					 &par, 1);
164994ea402SLinus Walleij 		if (ret) {
165994ea402SLinus Walleij 			dev_err(nt->dev, "failed to disable display backlight (%d)\n", ret);
166994ea402SLinus Walleij 			return ret;
167994ea402SLinus Walleij 		}
168994ea402SLinus Walleij 		return 0;
169994ea402SLinus Walleij 	}
170994ea402SLinus Walleij 
171994ea402SLinus Walleij 	/* Calculate the PWM duty cycle in n/256's */
172994ea402SLinus Walleij 	pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1);
173994ea402SLinus Walleij 	pwm_div = max(1,
174994ea402SLinus Walleij 		      ((FOSC * period_ns) / 256) /
175994ea402SLinus Walleij 		      SCALE_FACTOR_NS_DIV_MHZ);
176994ea402SLinus Walleij 
177994ea402SLinus Walleij 	/* Set up PWM dutycycle ONE byte (differs from the standard) */
178994ea402SLinus Walleij 	dev_dbg(nt->dev, "calculated duty cycle %02x\n", pwm_ratio);
179994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
180994ea402SLinus Walleij 				 &pwm_ratio, 1);
181994ea402SLinus Walleij 	if (ret < 0) {
182994ea402SLinus Walleij 		dev_err(nt->dev, "failed to set display PWM ratio (%d)\n", ret);
183994ea402SLinus Walleij 		return ret;
184994ea402SLinus Walleij 	}
185994ea402SLinus Walleij 
186994ea402SLinus Walleij 	/*
187994ea402SLinus Walleij 	 * Sequence to write PWMDIV:
188994ea402SLinus Walleij 	 *	address		data
189994ea402SLinus Walleij 	 *	0xF3		0xAA   CMD2 Unlock
190994ea402SLinus Walleij 	 *	0x00		0x01   Enter CMD2 page 0
191994ea402SLinus Walleij 	 *	0X7D		0x01   No reload MTP of CMD2 P1
192994ea402SLinus Walleij 	 *	0x22		PWMDIV
193994ea402SLinus Walleij 	 *	0x7F		0xAA   CMD2 page 1 lock
194994ea402SLinus Walleij 	 */
195994ea402SLinus Walleij 	par = 0xaa;
196994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
197994ea402SLinus Walleij 	if (ret < 0) {
198994ea402SLinus Walleij 		dev_err(nt->dev, "failed to unlock CMD 2 (%d)\n", ret);
199994ea402SLinus Walleij 		return ret;
200994ea402SLinus Walleij 	}
201994ea402SLinus Walleij 	par = 0x01;
202994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
203994ea402SLinus Walleij 	if (ret < 0) {
204994ea402SLinus Walleij 		dev_err(nt->dev, "failed to enter page 1 (%d)\n", ret);
205994ea402SLinus Walleij 		return ret;
206994ea402SLinus Walleij 	}
207994ea402SLinus Walleij 	par = 0x01;
208994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
209994ea402SLinus Walleij 	if (ret < 0) {
210994ea402SLinus Walleij 		dev_err(nt->dev, "failed to disable MTP reload (%d)\n", ret);
211994ea402SLinus Walleij 		return ret;
212994ea402SLinus Walleij 	}
213994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
214994ea402SLinus Walleij 	if (ret < 0) {
215994ea402SLinus Walleij 		dev_err(nt->dev, "failed to set PWM divisor (%d)\n", ret);
216994ea402SLinus Walleij 		return ret;
217994ea402SLinus Walleij 	}
218994ea402SLinus Walleij 	par = 0xaa;
219994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
220994ea402SLinus Walleij 	if (ret < 0) {
221994ea402SLinus Walleij 		dev_err(nt->dev, "failed to lock CMD 2 (%d)\n", ret);
222994ea402SLinus Walleij 		return ret;
223994ea402SLinus Walleij 	}
224994ea402SLinus Walleij 
225994ea402SLinus Walleij 	/* Enable backlight */
226994ea402SLinus Walleij 	par = 0x24;
227994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
228994ea402SLinus Walleij 				 &par, 1);
229994ea402SLinus Walleij 	if (ret < 0) {
230994ea402SLinus Walleij 		dev_err(nt->dev, "failed to enable display backlight (%d)\n", ret);
231994ea402SLinus Walleij 		return ret;
232994ea402SLinus Walleij 	}
233994ea402SLinus Walleij 
234994ea402SLinus Walleij 	return 0;
235994ea402SLinus Walleij }
236994ea402SLinus Walleij 
237994ea402SLinus Walleij static const struct backlight_ops nt35560_bl_ops = {
238994ea402SLinus Walleij 	.update_status = nt35560_set_brightness,
239994ea402SLinus Walleij };
240994ea402SLinus Walleij 
241994ea402SLinus Walleij static const struct backlight_properties nt35560_bl_props = {
242994ea402SLinus Walleij 	.type = BACKLIGHT_RAW,
243994ea402SLinus Walleij 	.brightness = 512,
244994ea402SLinus Walleij 	.max_brightness = 1023,
245994ea402SLinus Walleij };
246994ea402SLinus Walleij 
nt35560_read_id(struct nt35560 * nt)247994ea402SLinus Walleij static int nt35560_read_id(struct nt35560 *nt)
248994ea402SLinus Walleij {
249994ea402SLinus Walleij 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
250994ea402SLinus Walleij 	u8 vendor, version, panel;
251994ea402SLinus Walleij 	u16 val;
252994ea402SLinus Walleij 	int ret;
253994ea402SLinus Walleij 
254994ea402SLinus Walleij 	ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID1, &vendor, 1);
255994ea402SLinus Walleij 	if (ret < 0) {
256994ea402SLinus Walleij 		dev_err(nt->dev, "could not vendor ID byte\n");
257994ea402SLinus Walleij 		return ret;
258994ea402SLinus Walleij 	}
259994ea402SLinus Walleij 	ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID2, &version, 1);
260994ea402SLinus Walleij 	if (ret < 0) {
261994ea402SLinus Walleij 		dev_err(nt->dev, "could not read device version byte\n");
262994ea402SLinus Walleij 		return ret;
263994ea402SLinus Walleij 	}
264994ea402SLinus Walleij 	ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID3, &panel, 1);
265994ea402SLinus Walleij 	if (ret < 0) {
266994ea402SLinus Walleij 		dev_err(nt->dev, "could not read panel ID byte\n");
267994ea402SLinus Walleij 		return ret;
268994ea402SLinus Walleij 	}
269994ea402SLinus Walleij 
270994ea402SLinus Walleij 	if (vendor == 0x00) {
271994ea402SLinus Walleij 		dev_err(nt->dev, "device vendor ID is zero\n");
272994ea402SLinus Walleij 		return -ENODEV;
273994ea402SLinus Walleij 	}
274994ea402SLinus Walleij 
275994ea402SLinus Walleij 	val = (vendor << 8) | panel;
276994ea402SLinus Walleij 	switch (val) {
277994ea402SLinus Walleij 	case DISPLAY_SONY_ACX424AKP_ID1:
278994ea402SLinus Walleij 	case DISPLAY_SONY_ACX424AKP_ID2:
279994ea402SLinus Walleij 	case DISPLAY_SONY_ACX424AKP_ID3:
280e78089daSLinus Walleij 	case DISPLAY_SONY_ACX424AKP_ID4:
281994ea402SLinus Walleij 		dev_info(nt->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n",
282994ea402SLinus Walleij 			 vendor, version, panel);
283994ea402SLinus Walleij 		break;
284994ea402SLinus Walleij 	default:
285994ea402SLinus Walleij 		dev_info(nt->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n",
286994ea402SLinus Walleij 			 vendor, version, panel);
287994ea402SLinus Walleij 		break;
288994ea402SLinus Walleij 	}
289994ea402SLinus Walleij 
290994ea402SLinus Walleij 	return 0;
291994ea402SLinus Walleij }
292994ea402SLinus Walleij 
nt35560_power_on(struct nt35560 * nt)293994ea402SLinus Walleij static int nt35560_power_on(struct nt35560 *nt)
294994ea402SLinus Walleij {
295994ea402SLinus Walleij 	int ret;
296994ea402SLinus Walleij 
297994ea402SLinus Walleij 	ret = regulator_enable(nt->supply);
298994ea402SLinus Walleij 	if (ret) {
299994ea402SLinus Walleij 		dev_err(nt->dev, "failed to enable supply (%d)\n", ret);
300994ea402SLinus Walleij 		return ret;
301994ea402SLinus Walleij 	}
302994ea402SLinus Walleij 
303994ea402SLinus Walleij 	/* Assert RESET */
304994ea402SLinus Walleij 	gpiod_set_value_cansleep(nt->reset_gpio, 1);
305994ea402SLinus Walleij 	udelay(20);
306994ea402SLinus Walleij 	/* De-assert RESET */
307994ea402SLinus Walleij 	gpiod_set_value_cansleep(nt->reset_gpio, 0);
308994ea402SLinus Walleij 	usleep_range(11000, 20000);
309994ea402SLinus Walleij 
310994ea402SLinus Walleij 	return 0;
311994ea402SLinus Walleij }
312994ea402SLinus Walleij 
nt35560_power_off(struct nt35560 * nt)313994ea402SLinus Walleij static void nt35560_power_off(struct nt35560 *nt)
314994ea402SLinus Walleij {
315994ea402SLinus Walleij 	/* Assert RESET */
316994ea402SLinus Walleij 	gpiod_set_value_cansleep(nt->reset_gpio, 1);
317994ea402SLinus Walleij 	usleep_range(11000, 20000);
318994ea402SLinus Walleij 
319994ea402SLinus Walleij 	regulator_disable(nt->supply);
320994ea402SLinus Walleij }
321994ea402SLinus Walleij 
nt35560_prepare(struct drm_panel * panel)322994ea402SLinus Walleij static int nt35560_prepare(struct drm_panel *panel)
323994ea402SLinus Walleij {
324994ea402SLinus Walleij 	struct nt35560 *nt = panel_to_nt35560(panel);
325994ea402SLinus Walleij 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
326994ea402SLinus Walleij 	const u8 mddi = 3;
327994ea402SLinus Walleij 	int ret;
328994ea402SLinus Walleij 
329994ea402SLinus Walleij 	ret = nt35560_power_on(nt);
330994ea402SLinus Walleij 	if (ret)
331994ea402SLinus Walleij 		return ret;
332994ea402SLinus Walleij 
333994ea402SLinus Walleij 	ret = nt35560_read_id(nt);
334994ea402SLinus Walleij 	if (ret) {
335994ea402SLinus Walleij 		dev_err(nt->dev, "failed to read panel ID (%d)\n", ret);
336994ea402SLinus Walleij 		goto err_power_off;
337994ea402SLinus Walleij 	}
338994ea402SLinus Walleij 
339994ea402SLinus Walleij 	/* Enabe tearing mode: send TE (tearing effect) at VBLANK */
340994ea402SLinus Walleij 	ret = mipi_dsi_dcs_set_tear_on(dsi,
341994ea402SLinus Walleij 				       MIPI_DSI_DCS_TEAR_MODE_VBLANK);
342994ea402SLinus Walleij 	if (ret) {
343994ea402SLinus Walleij 		dev_err(nt->dev, "failed to enable vblank TE (%d)\n", ret);
344994ea402SLinus Walleij 		goto err_power_off;
345994ea402SLinus Walleij 	}
346994ea402SLinus Walleij 
347994ea402SLinus Walleij 	/*
348994ea402SLinus Walleij 	 * Set MDDI
349994ea402SLinus Walleij 	 *
350994ea402SLinus Walleij 	 * This presumably deactivates the Qualcomm MDDI interface and
351994ea402SLinus Walleij 	 * selects DSI, similar code is found in other drivers such as the
352994ea402SLinus Walleij 	 * Sharp LS043T1LE01 which makes us suspect that this panel may be
353994ea402SLinus Walleij 	 * using a Novatek NT35565 or similar display driver chip that shares
354994ea402SLinus Walleij 	 * this command. Due to the lack of documentation we cannot know for
355994ea402SLinus Walleij 	 * sure.
356994ea402SLinus Walleij 	 */
357994ea402SLinus Walleij 	ret = mipi_dsi_dcs_write(dsi, NT35560_DCS_SET_MDDI,
358994ea402SLinus Walleij 				 &mddi, sizeof(mddi));
359994ea402SLinus Walleij 	if (ret < 0) {
360994ea402SLinus Walleij 		dev_err(nt->dev, "failed to set MDDI (%d)\n", ret);
361994ea402SLinus Walleij 		goto err_power_off;
362994ea402SLinus Walleij 	}
363994ea402SLinus Walleij 
364994ea402SLinus Walleij 	/* Exit sleep mode */
365994ea402SLinus Walleij 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
366994ea402SLinus Walleij 	if (ret) {
367994ea402SLinus Walleij 		dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret);
368994ea402SLinus Walleij 		goto err_power_off;
369994ea402SLinus Walleij 	}
370994ea402SLinus Walleij 	msleep(140);
371994ea402SLinus Walleij 
372994ea402SLinus Walleij 	ret = mipi_dsi_dcs_set_display_on(dsi);
373994ea402SLinus Walleij 	if (ret) {
374994ea402SLinus Walleij 		dev_err(nt->dev, "failed to turn display on (%d)\n", ret);
375994ea402SLinus Walleij 		goto err_power_off;
376994ea402SLinus Walleij 	}
377994ea402SLinus Walleij 	if (nt->video_mode) {
378994ea402SLinus Walleij 		/* In video mode turn peripheral on */
379994ea402SLinus Walleij 		ret = mipi_dsi_turn_on_peripheral(dsi);
380994ea402SLinus Walleij 		if (ret) {
381994ea402SLinus Walleij 			dev_err(nt->dev, "failed to turn on peripheral\n");
382994ea402SLinus Walleij 			goto err_power_off;
383994ea402SLinus Walleij 		}
384994ea402SLinus Walleij 	}
385994ea402SLinus Walleij 
386994ea402SLinus Walleij 	return 0;
387994ea402SLinus Walleij 
388994ea402SLinus Walleij err_power_off:
389994ea402SLinus Walleij 	nt35560_power_off(nt);
390994ea402SLinus Walleij 	return ret;
391994ea402SLinus Walleij }
392994ea402SLinus Walleij 
nt35560_unprepare(struct drm_panel * panel)393994ea402SLinus Walleij static int nt35560_unprepare(struct drm_panel *panel)
394994ea402SLinus Walleij {
395994ea402SLinus Walleij 	struct nt35560 *nt = panel_to_nt35560(panel);
396994ea402SLinus Walleij 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
397994ea402SLinus Walleij 	int ret;
398994ea402SLinus Walleij 
399994ea402SLinus Walleij 	ret = mipi_dsi_dcs_set_display_off(dsi);
400994ea402SLinus Walleij 	if (ret) {
401994ea402SLinus Walleij 		dev_err(nt->dev, "failed to turn display off (%d)\n", ret);
402994ea402SLinus Walleij 		return ret;
403994ea402SLinus Walleij 	}
404994ea402SLinus Walleij 
405994ea402SLinus Walleij 	/* Enter sleep mode */
406994ea402SLinus Walleij 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
407994ea402SLinus Walleij 	if (ret) {
408994ea402SLinus Walleij 		dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret);
409994ea402SLinus Walleij 		return ret;
410994ea402SLinus Walleij 	}
411994ea402SLinus Walleij 	msleep(85);
412994ea402SLinus Walleij 
413994ea402SLinus Walleij 	nt35560_power_off(nt);
414994ea402SLinus Walleij 
415994ea402SLinus Walleij 	return 0;
416994ea402SLinus Walleij }
417994ea402SLinus Walleij 
418994ea402SLinus Walleij 
nt35560_get_modes(struct drm_panel * panel,struct drm_connector * connector)419994ea402SLinus Walleij static int nt35560_get_modes(struct drm_panel *panel,
420994ea402SLinus Walleij 			     struct drm_connector *connector)
421994ea402SLinus Walleij {
422994ea402SLinus Walleij 	struct nt35560 *nt = panel_to_nt35560(panel);
423de45f0a3SLinus Walleij 	const struct nt35560_config *conf = nt->conf;
424994ea402SLinus Walleij 	struct drm_display_mode *mode;
425994ea402SLinus Walleij 
426994ea402SLinus Walleij 	if (nt->video_mode)
427994ea402SLinus Walleij 		mode = drm_mode_duplicate(connector->dev,
428de45f0a3SLinus Walleij 					  conf->vid_mode);
429994ea402SLinus Walleij 	else
430994ea402SLinus Walleij 		mode = drm_mode_duplicate(connector->dev,
431de45f0a3SLinus Walleij 					  conf->cmd_mode);
432994ea402SLinus Walleij 	if (!mode) {
433994ea402SLinus Walleij 		dev_err(panel->dev, "bad mode or failed to add mode\n");
434994ea402SLinus Walleij 		return -EINVAL;
435994ea402SLinus Walleij 	}
436994ea402SLinus Walleij 	drm_mode_set_name(mode);
437994ea402SLinus Walleij 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
438994ea402SLinus Walleij 
439994ea402SLinus Walleij 	connector->display_info.width_mm = mode->width_mm;
440994ea402SLinus Walleij 	connector->display_info.height_mm = mode->height_mm;
441994ea402SLinus Walleij 
442994ea402SLinus Walleij 	drm_mode_probed_add(connector, mode);
443994ea402SLinus Walleij 
444994ea402SLinus Walleij 	return 1; /* Number of modes */
445994ea402SLinus Walleij }
446994ea402SLinus Walleij 
447994ea402SLinus Walleij static const struct drm_panel_funcs nt35560_drm_funcs = {
448994ea402SLinus Walleij 	.unprepare = nt35560_unprepare,
449994ea402SLinus Walleij 	.prepare = nt35560_prepare,
450994ea402SLinus Walleij 	.get_modes = nt35560_get_modes,
451994ea402SLinus Walleij };
452994ea402SLinus Walleij 
nt35560_probe(struct mipi_dsi_device * dsi)453994ea402SLinus Walleij static int nt35560_probe(struct mipi_dsi_device *dsi)
454994ea402SLinus Walleij {
455994ea402SLinus Walleij 	struct device *dev = &dsi->dev;
456994ea402SLinus Walleij 	struct nt35560 *nt;
457994ea402SLinus Walleij 	int ret;
458994ea402SLinus Walleij 
459994ea402SLinus Walleij 	nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL);
460994ea402SLinus Walleij 	if (!nt)
461994ea402SLinus Walleij 		return -ENOMEM;
462994ea402SLinus Walleij 	nt->video_mode = of_property_read_bool(dev->of_node,
463994ea402SLinus Walleij 						"enforce-video-mode");
464994ea402SLinus Walleij 
465994ea402SLinus Walleij 	mipi_dsi_set_drvdata(dsi, nt);
466994ea402SLinus Walleij 	nt->dev = dev;
467994ea402SLinus Walleij 
468de45f0a3SLinus Walleij 	nt->conf = of_device_get_match_data(dev);
469de45f0a3SLinus Walleij 	if (!nt->conf) {
470de45f0a3SLinus Walleij 		dev_err(dev, "missing device configuration\n");
471de45f0a3SLinus Walleij 		return -ENODEV;
472de45f0a3SLinus Walleij 	}
473de45f0a3SLinus Walleij 
474994ea402SLinus Walleij 	dsi->lanes = 2;
475994ea402SLinus Walleij 	dsi->format = MIPI_DSI_FMT_RGB888;
476994ea402SLinus Walleij 	/*
477994ea402SLinus Walleij 	 * FIXME: these come from the ST-Ericsson vendor driver for the
478994ea402SLinus Walleij 	 * HREF520 and seems to reflect limitations in the PLLs on that
479994ea402SLinus Walleij 	 * platform, if you have the datasheet, please cross-check the
480994ea402SLinus Walleij 	 * actual max rates.
481994ea402SLinus Walleij 	 */
482994ea402SLinus Walleij 	dsi->lp_rate = 19200000;
483994ea402SLinus Walleij 	dsi->hs_rate = 420160000;
484994ea402SLinus Walleij 
485994ea402SLinus Walleij 	if (nt->video_mode)
486994ea402SLinus Walleij 		/* Burst mode using event for sync */
487994ea402SLinus Walleij 		dsi->mode_flags =
488994ea402SLinus Walleij 			MIPI_DSI_MODE_VIDEO |
489994ea402SLinus Walleij 			MIPI_DSI_MODE_VIDEO_BURST;
490994ea402SLinus Walleij 	else
491994ea402SLinus Walleij 		dsi->mode_flags =
492994ea402SLinus Walleij 			MIPI_DSI_CLOCK_NON_CONTINUOUS;
493994ea402SLinus Walleij 
494994ea402SLinus Walleij 	nt->supply = devm_regulator_get(dev, "vddi");
495994ea402SLinus Walleij 	if (IS_ERR(nt->supply))
496994ea402SLinus Walleij 		return PTR_ERR(nt->supply);
497994ea402SLinus Walleij 
498994ea402SLinus Walleij 	/* This asserts RESET by default */
499994ea402SLinus Walleij 	nt->reset_gpio = devm_gpiod_get_optional(dev, "reset",
500994ea402SLinus Walleij 						 GPIOD_OUT_HIGH);
501994ea402SLinus Walleij 	if (IS_ERR(nt->reset_gpio))
502994ea402SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),
503994ea402SLinus Walleij 				     "failed to request GPIO\n");
504994ea402SLinus Walleij 
505994ea402SLinus Walleij 	drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs,
506994ea402SLinus Walleij 		       DRM_MODE_CONNECTOR_DSI);
507994ea402SLinus Walleij 
508994ea402SLinus Walleij 	nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt,
509994ea402SLinus Walleij 					&nt35560_bl_ops, &nt35560_bl_props);
510994ea402SLinus Walleij 	if (IS_ERR(nt->panel.backlight))
511994ea402SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(nt->panel.backlight),
512994ea402SLinus Walleij 				     "failed to register backlight device\n");
513994ea402SLinus Walleij 
514994ea402SLinus Walleij 	drm_panel_add(&nt->panel);
515994ea402SLinus Walleij 
516994ea402SLinus Walleij 	ret = mipi_dsi_attach(dsi);
517994ea402SLinus Walleij 	if (ret < 0) {
518994ea402SLinus Walleij 		drm_panel_remove(&nt->panel);
519994ea402SLinus Walleij 		return ret;
520994ea402SLinus Walleij 	}
521994ea402SLinus Walleij 
522994ea402SLinus Walleij 	return 0;
523994ea402SLinus Walleij }
524994ea402SLinus Walleij 
nt35560_remove(struct mipi_dsi_device * dsi)525*79abca2bSUwe Kleine-König static void nt35560_remove(struct mipi_dsi_device *dsi)
526994ea402SLinus Walleij {
527994ea402SLinus Walleij 	struct nt35560 *nt = mipi_dsi_get_drvdata(dsi);
528994ea402SLinus Walleij 
529994ea402SLinus Walleij 	mipi_dsi_detach(dsi);
530994ea402SLinus Walleij 	drm_panel_remove(&nt->panel);
531994ea402SLinus Walleij }
532994ea402SLinus Walleij 
533994ea402SLinus Walleij static const struct of_device_id nt35560_of_match[] = {
534de45f0a3SLinus Walleij 	{
535de45f0a3SLinus Walleij 		.compatible = "sony,acx424akp",
536de45f0a3SLinus Walleij 		.data = &sony_acx424akp_data,
537de45f0a3SLinus Walleij 	},
538de45f0a3SLinus Walleij 	{
539de45f0a3SLinus Walleij 		.compatible = "sony,acx424akm",
540de45f0a3SLinus Walleij 		.data = &sony_acx424akm_data,
541de45f0a3SLinus Walleij 	},
542994ea402SLinus Walleij 	{ /* sentinel */ }
543994ea402SLinus Walleij };
544994ea402SLinus Walleij MODULE_DEVICE_TABLE(of, nt35560_of_match);
545994ea402SLinus Walleij 
546994ea402SLinus Walleij static struct mipi_dsi_driver nt35560_driver = {
547994ea402SLinus Walleij 	.probe = nt35560_probe,
548994ea402SLinus Walleij 	.remove = nt35560_remove,
549994ea402SLinus Walleij 	.driver = {
550994ea402SLinus Walleij 		.name = "panel-novatek-nt35560",
551994ea402SLinus Walleij 		.of_match_table = nt35560_of_match,
552994ea402SLinus Walleij 	},
553994ea402SLinus Walleij };
554994ea402SLinus Walleij module_mipi_dsi_driver(nt35560_driver);
555994ea402SLinus Walleij 
556994ea402SLinus Walleij MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
557994ea402SLinus Walleij MODULE_DESCRIPTION("MIPI-DSI Novatek NT35560 Panel Driver");
558994ea402SLinus Walleij MODULE_LICENSE("GPL v2");
559