xref: /linux/drivers/gpu/drm/panel/panel-renesas-r69328.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1*9e0f93f7SMaxim Schwalm // SPDX-License-Identifier: GPL-2.0
2*9e0f93f7SMaxim Schwalm 
3*9e0f93f7SMaxim Schwalm #include <linux/array_size.h>
4*9e0f93f7SMaxim Schwalm #include <linux/delay.h>
5*9e0f93f7SMaxim Schwalm #include <linux/err.h>
6*9e0f93f7SMaxim Schwalm #include <linux/gpio/consumer.h>
7*9e0f93f7SMaxim Schwalm #include <linux/mod_devicetable.h>
8*9e0f93f7SMaxim Schwalm #include <linux/module.h>
9*9e0f93f7SMaxim Schwalm #include <linux/property.h>
10*9e0f93f7SMaxim Schwalm #include <linux/regulator/consumer.h>
11*9e0f93f7SMaxim Schwalm 
12*9e0f93f7SMaxim Schwalm #include <video/mipi_display.h>
13*9e0f93f7SMaxim Schwalm 
14*9e0f93f7SMaxim Schwalm #include <drm/drm_mipi_dsi.h>
15*9e0f93f7SMaxim Schwalm #include <drm/drm_modes.h>
16*9e0f93f7SMaxim Schwalm #include <drm/drm_panel.h>
17*9e0f93f7SMaxim Schwalm 
18*9e0f93f7SMaxim Schwalm #define R69328_MACP		0xb0 /* Manufacturer Access CMD Protect */
19*9e0f93f7SMaxim Schwalm #define   R69328_MACP_ON	0x03
20*9e0f93f7SMaxim Schwalm #define   R69328_MACP_OFF	0x04
21*9e0f93f7SMaxim Schwalm 
22*9e0f93f7SMaxim Schwalm #define R69328_GAMMA_SET_A	0xc8 /* Gamma Setting A */
23*9e0f93f7SMaxim Schwalm #define R69328_GAMMA_SET_B	0xc9 /* Gamma Setting B */
24*9e0f93f7SMaxim Schwalm #define R69328_GAMMA_SET_C	0xca /* Gamma Setting C */
25*9e0f93f7SMaxim Schwalm 
26*9e0f93f7SMaxim Schwalm #define R69328_POWER_SET	0xd1
27*9e0f93f7SMaxim Schwalm 
28*9e0f93f7SMaxim Schwalm struct renesas_r69328 {
29*9e0f93f7SMaxim Schwalm 	struct drm_panel panel;
30*9e0f93f7SMaxim Schwalm 	struct mipi_dsi_device *dsi;
31*9e0f93f7SMaxim Schwalm 
32*9e0f93f7SMaxim Schwalm 	struct regulator *vdd_supply;
33*9e0f93f7SMaxim Schwalm 	struct regulator *vddio_supply;
34*9e0f93f7SMaxim Schwalm 	struct gpio_desc *reset_gpio;
35*9e0f93f7SMaxim Schwalm 
36*9e0f93f7SMaxim Schwalm 	bool prepared;
37*9e0f93f7SMaxim Schwalm };
38*9e0f93f7SMaxim Schwalm 
39*9e0f93f7SMaxim Schwalm static inline struct renesas_r69328 *to_renesas_r69328(struct drm_panel *panel)
40*9e0f93f7SMaxim Schwalm {
41*9e0f93f7SMaxim Schwalm 	return container_of(panel, struct renesas_r69328, panel);
42*9e0f93f7SMaxim Schwalm }
43*9e0f93f7SMaxim Schwalm 
44*9e0f93f7SMaxim Schwalm static void renesas_r69328_reset(struct renesas_r69328 *priv)
45*9e0f93f7SMaxim Schwalm {
46*9e0f93f7SMaxim Schwalm 	gpiod_set_value_cansleep(priv->reset_gpio, 1);
47*9e0f93f7SMaxim Schwalm 	usleep_range(10000, 11000);
48*9e0f93f7SMaxim Schwalm 	gpiod_set_value_cansleep(priv->reset_gpio, 0);
49*9e0f93f7SMaxim Schwalm 	usleep_range(2000, 3000);
50*9e0f93f7SMaxim Schwalm }
51*9e0f93f7SMaxim Schwalm 
52*9e0f93f7SMaxim Schwalm static int renesas_r69328_prepare(struct drm_panel *panel)
53*9e0f93f7SMaxim Schwalm {
54*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv = to_renesas_r69328(panel);
55*9e0f93f7SMaxim Schwalm 	struct device *dev = &priv->dsi->dev;
56*9e0f93f7SMaxim Schwalm 	int ret;
57*9e0f93f7SMaxim Schwalm 
58*9e0f93f7SMaxim Schwalm 	if (priv->prepared)
59*9e0f93f7SMaxim Schwalm 		return 0;
60*9e0f93f7SMaxim Schwalm 
61*9e0f93f7SMaxim Schwalm 	ret = regulator_enable(priv->vdd_supply);
62*9e0f93f7SMaxim Schwalm 	if (ret) {
63*9e0f93f7SMaxim Schwalm 		dev_err(dev, "failed to enable vdd power supply\n");
64*9e0f93f7SMaxim Schwalm 		return ret;
65*9e0f93f7SMaxim Schwalm 	}
66*9e0f93f7SMaxim Schwalm 
67*9e0f93f7SMaxim Schwalm 	usleep_range(10000, 11000);
68*9e0f93f7SMaxim Schwalm 
69*9e0f93f7SMaxim Schwalm 	ret = regulator_enable(priv->vddio_supply);
70*9e0f93f7SMaxim Schwalm 	if (ret < 0) {
71*9e0f93f7SMaxim Schwalm 		dev_err(dev, "failed to enable vddio power supply\n");
72*9e0f93f7SMaxim Schwalm 		return ret;
73*9e0f93f7SMaxim Schwalm 	}
74*9e0f93f7SMaxim Schwalm 
75*9e0f93f7SMaxim Schwalm 	usleep_range(10000, 11000);
76*9e0f93f7SMaxim Schwalm 
77*9e0f93f7SMaxim Schwalm 	renesas_r69328_reset(priv);
78*9e0f93f7SMaxim Schwalm 
79*9e0f93f7SMaxim Schwalm 	priv->prepared = true;
80*9e0f93f7SMaxim Schwalm 	return 0;
81*9e0f93f7SMaxim Schwalm }
82*9e0f93f7SMaxim Schwalm 
83*9e0f93f7SMaxim Schwalm static int renesas_r69328_enable(struct drm_panel *panel)
84*9e0f93f7SMaxim Schwalm {
85*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv = to_renesas_r69328(panel);
86*9e0f93f7SMaxim Schwalm 	struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
87*9e0f93f7SMaxim Schwalm 
88*9e0f93f7SMaxim Schwalm 	/* Set address mode */
89*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
90*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4);
91*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
92*9e0f93f7SMaxim Schwalm 
93*9e0f93f7SMaxim Schwalm 	mipi_dsi_msleep(&ctx, 100);
94*9e0f93f7SMaxim Schwalm 
95*9e0f93f7SMaxim Schwalm 	/* MACP Off */
96*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_OFF);
97*9e0f93f7SMaxim Schwalm 
98*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_POWER_SET, 0x14, 0x1d,
99*9e0f93f7SMaxim Schwalm 					 0x21, 0x67, 0x11, 0x9a);
100*9e0f93f7SMaxim Schwalm 
101*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_A, 0x00, 0x1a,
102*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
103*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a,
104*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
105*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00);
106*9e0f93f7SMaxim Schwalm 
107*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_B, 0x00, 0x1a,
108*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
109*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a,
110*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
111*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00);
112*9e0f93f7SMaxim Schwalm 
113*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_C, 0x00, 0x1a,
114*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
115*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a,
116*9e0f93f7SMaxim Schwalm 					 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13,
117*9e0f93f7SMaxim Schwalm 					 0x11, 0x18, 0x1e, 0x1c, 0x00);
118*9e0f93f7SMaxim Schwalm 
119*9e0f93f7SMaxim Schwalm 	/* MACP On */
120*9e0f93f7SMaxim Schwalm 	mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_ON);
121*9e0f93f7SMaxim Schwalm 
122*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_set_display_on_multi(&ctx);
123*9e0f93f7SMaxim Schwalm 	mipi_dsi_msleep(&ctx, 50);
124*9e0f93f7SMaxim Schwalm 
125*9e0f93f7SMaxim Schwalm 	return 0;
126*9e0f93f7SMaxim Schwalm }
127*9e0f93f7SMaxim Schwalm 
128*9e0f93f7SMaxim Schwalm static int renesas_r69328_disable(struct drm_panel *panel)
129*9e0f93f7SMaxim Schwalm {
130*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv = to_renesas_r69328(panel);
131*9e0f93f7SMaxim Schwalm 	struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
132*9e0f93f7SMaxim Schwalm 
133*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_set_display_off_multi(&ctx);
134*9e0f93f7SMaxim Schwalm 	mipi_dsi_msleep(&ctx, 60);
135*9e0f93f7SMaxim Schwalm 	mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
136*9e0f93f7SMaxim Schwalm 
137*9e0f93f7SMaxim Schwalm 	return 0;
138*9e0f93f7SMaxim Schwalm }
139*9e0f93f7SMaxim Schwalm 
140*9e0f93f7SMaxim Schwalm static int renesas_r69328_unprepare(struct drm_panel *panel)
141*9e0f93f7SMaxim Schwalm {
142*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv = to_renesas_r69328(panel);
143*9e0f93f7SMaxim Schwalm 
144*9e0f93f7SMaxim Schwalm 	if (!priv->prepared)
145*9e0f93f7SMaxim Schwalm 		return 0;
146*9e0f93f7SMaxim Schwalm 
147*9e0f93f7SMaxim Schwalm 	gpiod_set_value_cansleep(priv->reset_gpio, 1);
148*9e0f93f7SMaxim Schwalm 
149*9e0f93f7SMaxim Schwalm 	usleep_range(5000, 6000);
150*9e0f93f7SMaxim Schwalm 
151*9e0f93f7SMaxim Schwalm 	regulator_disable(priv->vddio_supply);
152*9e0f93f7SMaxim Schwalm 	regulator_disable(priv->vdd_supply);
153*9e0f93f7SMaxim Schwalm 
154*9e0f93f7SMaxim Schwalm 	priv->prepared = false;
155*9e0f93f7SMaxim Schwalm 	return 0;
156*9e0f93f7SMaxim Schwalm }
157*9e0f93f7SMaxim Schwalm 
158*9e0f93f7SMaxim Schwalm static const struct drm_display_mode renesas_r69328_mode = {
159*9e0f93f7SMaxim Schwalm 	.clock = (720 + 92 + 62 + 4) * (1280 + 6 + 3 + 1) * 60 / 1000,
160*9e0f93f7SMaxim Schwalm 	.hdisplay = 720,
161*9e0f93f7SMaxim Schwalm 	.hsync_start = 720 + 92,
162*9e0f93f7SMaxim Schwalm 	.hsync_end = 720 + 92 + 62,
163*9e0f93f7SMaxim Schwalm 	.htotal = 720 + 92 + 62 + 4,
164*9e0f93f7SMaxim Schwalm 	.vdisplay = 1280,
165*9e0f93f7SMaxim Schwalm 	.vsync_start = 1280 + 6,
166*9e0f93f7SMaxim Schwalm 	.vsync_end = 1280 + 6 + 3,
167*9e0f93f7SMaxim Schwalm 	.vtotal = 1280 + 6 + 3 + 1,
168*9e0f93f7SMaxim Schwalm 	.width_mm = 59,
169*9e0f93f7SMaxim Schwalm 	.height_mm = 105,
170*9e0f93f7SMaxim Schwalm };
171*9e0f93f7SMaxim Schwalm 
172*9e0f93f7SMaxim Schwalm static int renesas_r69328_get_modes(struct drm_panel *panel,
173*9e0f93f7SMaxim Schwalm 				    struct drm_connector *connector)
174*9e0f93f7SMaxim Schwalm {
175*9e0f93f7SMaxim Schwalm 	struct drm_display_mode *mode;
176*9e0f93f7SMaxim Schwalm 
177*9e0f93f7SMaxim Schwalm 	mode = drm_mode_duplicate(connector->dev, &renesas_r69328_mode);
178*9e0f93f7SMaxim Schwalm 	if (!mode)
179*9e0f93f7SMaxim Schwalm 		return -ENOMEM;
180*9e0f93f7SMaxim Schwalm 
181*9e0f93f7SMaxim Schwalm 	drm_mode_set_name(mode);
182*9e0f93f7SMaxim Schwalm 
183*9e0f93f7SMaxim Schwalm 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
184*9e0f93f7SMaxim Schwalm 	connector->display_info.width_mm = mode->width_mm;
185*9e0f93f7SMaxim Schwalm 	connector->display_info.height_mm = mode->height_mm;
186*9e0f93f7SMaxim Schwalm 	drm_mode_probed_add(connector, mode);
187*9e0f93f7SMaxim Schwalm 
188*9e0f93f7SMaxim Schwalm 	return 1;
189*9e0f93f7SMaxim Schwalm }
190*9e0f93f7SMaxim Schwalm 
191*9e0f93f7SMaxim Schwalm static const struct drm_panel_funcs renesas_r69328_panel_funcs = {
192*9e0f93f7SMaxim Schwalm 	.prepare = renesas_r69328_prepare,
193*9e0f93f7SMaxim Schwalm 	.enable = renesas_r69328_enable,
194*9e0f93f7SMaxim Schwalm 	.disable = renesas_r69328_disable,
195*9e0f93f7SMaxim Schwalm 	.unprepare = renesas_r69328_unprepare,
196*9e0f93f7SMaxim Schwalm 	.get_modes = renesas_r69328_get_modes,
197*9e0f93f7SMaxim Schwalm };
198*9e0f93f7SMaxim Schwalm 
199*9e0f93f7SMaxim Schwalm static int renesas_r69328_probe(struct mipi_dsi_device *dsi)
200*9e0f93f7SMaxim Schwalm {
201*9e0f93f7SMaxim Schwalm 	struct device *dev = &dsi->dev;
202*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv;
203*9e0f93f7SMaxim Schwalm 	int ret;
204*9e0f93f7SMaxim Schwalm 
205*9e0f93f7SMaxim Schwalm 	priv = devm_drm_panel_alloc(dev, struct renesas_r69328, panel,
206*9e0f93f7SMaxim Schwalm 				    &renesas_r69328_panel_funcs,
207*9e0f93f7SMaxim Schwalm 				    DRM_MODE_CONNECTOR_DSI);
208*9e0f93f7SMaxim Schwalm 	if (IS_ERR(priv))
209*9e0f93f7SMaxim Schwalm 		return PTR_ERR(priv);
210*9e0f93f7SMaxim Schwalm 
211*9e0f93f7SMaxim Schwalm 	priv->vdd_supply = devm_regulator_get(dev, "vdd");
212*9e0f93f7SMaxim Schwalm 	if (IS_ERR(priv->vdd_supply))
213*9e0f93f7SMaxim Schwalm 		return dev_err_probe(dev, PTR_ERR(priv->vdd_supply),
214*9e0f93f7SMaxim Schwalm 				     "Failed to get vdd-supply\n");
215*9e0f93f7SMaxim Schwalm 
216*9e0f93f7SMaxim Schwalm 	priv->vddio_supply = devm_regulator_get(dev, "vddio");
217*9e0f93f7SMaxim Schwalm 	if (IS_ERR(priv->vddio_supply))
218*9e0f93f7SMaxim Schwalm 		return dev_err_probe(dev, PTR_ERR(priv->vddio_supply),
219*9e0f93f7SMaxim Schwalm 				     "Failed to get vddio-supply\n");
220*9e0f93f7SMaxim Schwalm 
221*9e0f93f7SMaxim Schwalm 	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
222*9e0f93f7SMaxim Schwalm 						   GPIOD_OUT_LOW);
223*9e0f93f7SMaxim Schwalm 	if (IS_ERR(priv->reset_gpio))
224*9e0f93f7SMaxim Schwalm 		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
225*9e0f93f7SMaxim Schwalm 				     "Failed to get reset-gpios\n");
226*9e0f93f7SMaxim Schwalm 
227*9e0f93f7SMaxim Schwalm 	priv->dsi = dsi;
228*9e0f93f7SMaxim Schwalm 	mipi_dsi_set_drvdata(dsi, priv);
229*9e0f93f7SMaxim Schwalm 
230*9e0f93f7SMaxim Schwalm 	dsi->lanes = 4;
231*9e0f93f7SMaxim Schwalm 	dsi->format = MIPI_DSI_FMT_RGB888;
232*9e0f93f7SMaxim Schwalm 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
233*9e0f93f7SMaxim Schwalm 			  MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
234*9e0f93f7SMaxim Schwalm 
235*9e0f93f7SMaxim Schwalm 	ret = drm_panel_of_backlight(&priv->panel);
236*9e0f93f7SMaxim Schwalm 	if (ret)
237*9e0f93f7SMaxim Schwalm 		return dev_err_probe(dev, ret, "Failed to get backlight\n");
238*9e0f93f7SMaxim Schwalm 
239*9e0f93f7SMaxim Schwalm 	drm_panel_add(&priv->panel);
240*9e0f93f7SMaxim Schwalm 
241*9e0f93f7SMaxim Schwalm 	ret = mipi_dsi_attach(dsi);
242*9e0f93f7SMaxim Schwalm 	if (ret) {
243*9e0f93f7SMaxim Schwalm 		drm_panel_remove(&priv->panel);
244*9e0f93f7SMaxim Schwalm 		return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
245*9e0f93f7SMaxim Schwalm 	}
246*9e0f93f7SMaxim Schwalm 
247*9e0f93f7SMaxim Schwalm 	return 0;
248*9e0f93f7SMaxim Schwalm }
249*9e0f93f7SMaxim Schwalm 
250*9e0f93f7SMaxim Schwalm static void renesas_r69328_remove(struct mipi_dsi_device *dsi)
251*9e0f93f7SMaxim Schwalm {
252*9e0f93f7SMaxim Schwalm 	struct renesas_r69328 *priv = mipi_dsi_get_drvdata(dsi);
253*9e0f93f7SMaxim Schwalm 	int ret;
254*9e0f93f7SMaxim Schwalm 
255*9e0f93f7SMaxim Schwalm 	ret = mipi_dsi_detach(dsi);
256*9e0f93f7SMaxim Schwalm 	if (ret)
257*9e0f93f7SMaxim Schwalm 		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
258*9e0f93f7SMaxim Schwalm 
259*9e0f93f7SMaxim Schwalm 	drm_panel_remove(&priv->panel);
260*9e0f93f7SMaxim Schwalm }
261*9e0f93f7SMaxim Schwalm 
262*9e0f93f7SMaxim Schwalm static const struct of_device_id renesas_r69328_of_match[] = {
263*9e0f93f7SMaxim Schwalm 	{ .compatible = "jdi,dx12d100vm0eaa" },
264*9e0f93f7SMaxim Schwalm 	{ /* sentinel */ }
265*9e0f93f7SMaxim Schwalm };
266*9e0f93f7SMaxim Schwalm MODULE_DEVICE_TABLE(of, renesas_r69328_of_match);
267*9e0f93f7SMaxim Schwalm 
268*9e0f93f7SMaxim Schwalm static struct mipi_dsi_driver renesas_r69328_driver = {
269*9e0f93f7SMaxim Schwalm 	.probe = renesas_r69328_probe,
270*9e0f93f7SMaxim Schwalm 	.remove = renesas_r69328_remove,
271*9e0f93f7SMaxim Schwalm 	.driver = {
272*9e0f93f7SMaxim Schwalm 		.name = "panel-renesas-r69328",
273*9e0f93f7SMaxim Schwalm 		.of_match_table = renesas_r69328_of_match,
274*9e0f93f7SMaxim Schwalm 	},
275*9e0f93f7SMaxim Schwalm };
276*9e0f93f7SMaxim Schwalm module_mipi_dsi_driver(renesas_r69328_driver);
277*9e0f93f7SMaxim Schwalm 
278*9e0f93f7SMaxim Schwalm MODULE_AUTHOR("Maxim Schwalm <maxim.schwalm@gmail.com>");
279*9e0f93f7SMaxim Schwalm MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
280*9e0f93f7SMaxim Schwalm MODULE_DESCRIPTION("Renesas R69328-based panel driver");
281*9e0f93f7SMaxim Schwalm MODULE_LICENSE("GPL");
282