blob: 082f5bc3d0a01306188c3f66f8d977dde4146c7d [file] [log] [blame]
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Renesas R69328 panel driver
4 *
5 * Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
6 */
7
Tom Riniabb9a042024-05-18 20:20:43 -06008#include <common.h>
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +03009#include <backlight.h>
10#include <dm.h>
11#include <panel.h>
12#include <log.h>
13#include <misc.h>
14#include <mipi_display.h>
15#include <mipi_dsi.h>
16#include <asm/gpio.h>
17#include <linux/delay.h>
18#include <linux/err.h>
19#include <power/regulator.h>
20
21/*
Michal Simekcc046dc2024-04-16 08:55:19 +020022 * The datasheet is not publicly available, all values are
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +030023 * taken from the downstream. If you have access to datasheets,
24 * corrections are welcome.
25 */
26
27#define R69328_MACP 0xB0 /* Manufacturer Command Access Protect */
28
29#define R69328_GAMMA_SET_A 0xC8 /* Gamma Setting A */
30#define R69328_GAMMA_SET_B 0xC9 /* Gamma Setting B */
31#define R69328_GAMMA_SET_C 0xCA /* Gamma Setting C */
32
33#define R69328_POWER_SET 0xD1
34
35struct renesas_r69328_priv {
36 struct udevice *backlight;
37
38 struct gpio_desc enable_gpio;
39 struct gpio_desc reset_gpio;
40};
41
42static const u8 address_mode[] = {
43 MIPI_DCS_SET_ADDRESS_MODE
44};
45
46#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
47 static const u8 b[] = { cmd, seq }; \
48 int ret; \
49 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
50 if (ret < 0) \
51 return ret; \
52 } while (0)
53
54static struct display_timing default_timing = {
55 .pixelclock.typ = 68000000,
56 .hactive.typ = 720,
57 .hfront_porch.typ = 92,
58 .hback_porch.typ = 62,
59 .hsync_len.typ = 4,
60 .vactive.typ = 1280,
61 .vfront_porch.typ = 6,
62 .vback_porch.typ = 3,
63 .vsync_len.typ = 1,
64};
65
66static int renesas_r69328_enable_backlight(struct udevice *dev)
67{
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +030068 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
69 struct mipi_dsi_device *dsi = plat->device;
70 int ret;
71
72 mipi_dsi_dcs_write_buffer(dsi, address_mode,
73 sizeof(address_mode));
74
75 ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
76 if (ret < 0) {
77 log_err("failed to set pixel format: %d\n", ret);
78 return ret;
79 }
80
81 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
82 if (ret < 0) {
83 log_err("failed to exit sleep mode: %d\n", ret);
84 return ret;
85 }
86
87 mdelay(100);
88
89 /* MACP Off */
90 dsi_generic_write_seq(dsi, R69328_MACP, 0x04);
91
92 dsi_generic_write_seq(dsi, R69328_POWER_SET, 0x14,
93 0x1d, 0x21, 0x67, 0x11, 0x9a);
94
95 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_A, 0x00,
96 0x1a, 0x20, 0x28, 0x25, 0x24,
97 0x26, 0x15, 0x13, 0x11, 0x18,
98 0x1e, 0x1c, 0x00, 0x00, 0x1a,
99 0x20, 0x28, 0x25, 0x24, 0x26,
100 0x15, 0x13, 0x11, 0x18, 0x1e,
101 0x1c, 0x00);
102 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_B, 0x00,
103 0x1a, 0x20, 0x28, 0x25, 0x24,
104 0x26, 0x15, 0x13, 0x11, 0x18,
105 0x1e, 0x1c, 0x00, 0x00, 0x1a,
106 0x20, 0x28, 0x25, 0x24, 0x26,
107 0x15, 0x13, 0x11, 0x18, 0x1e,
108 0x1c, 0x00);
109 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_C, 0x00,
110 0x1a, 0x20, 0x28, 0x25, 0x24,
111 0x26, 0x15, 0x13, 0x11, 0x18,
112 0x1e, 0x1c, 0x00, 0x00, 0x1a,
113 0x20, 0x28, 0x25, 0x24, 0x26,
114 0x15, 0x13, 0x11, 0x18, 0x1e,
115 0x1c, 0x00);
116
117 /* MACP On */
118 dsi_generic_write_seq(dsi, R69328_MACP, 0x03);
119
120 ret = mipi_dsi_dcs_set_display_on(dsi);
121 if (ret < 0) {
122 log_err("failed to set display on: %d\n", ret);
123 return ret;
124 }
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300125 mdelay(50);
126
Svyatoslav Ryhel8a13fd22024-01-31 08:57:21 +0200127 return 0;
128}
129
130static int renesas_r69328_set_backlight(struct udevice *dev, int percent)
131{
132 struct renesas_r69328_priv *priv = dev_get_priv(dev);
133 int ret;
134
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300135 ret = backlight_enable(priv->backlight);
136 if (ret)
137 return ret;
138
Svyatoslav Ryhel8a13fd22024-01-31 08:57:21 +0200139 mdelay(5);
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300140
Svyatoslav Ryhel8a13fd22024-01-31 08:57:21 +0200141 return backlight_set_brightness(priv->backlight, percent);
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300142}
143
144static int renesas_r69328_timings(struct udevice *dev,
145 struct display_timing *timing)
146{
147 memcpy(timing, &default_timing, sizeof(*timing));
148 return 0;
149}
150
151static int renesas_r69328_of_to_plat(struct udevice *dev)
152{
153 struct renesas_r69328_priv *priv = dev_get_priv(dev);
154 int ret;
155
156 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
157 "backlight", &priv->backlight);
158 if (ret) {
159 log_err("cannot get backlight: ret = %d\n", ret);
160 return ret;
161 }
162
163 ret = gpio_request_by_name(dev, "enable-gpios", 0,
164 &priv->enable_gpio, GPIOD_IS_OUT);
165 if (ret) {
166 log_err("could not decode enable-gpios (%d)\n", ret);
167 return ret;
168 }
169
170 ret = gpio_request_by_name(dev, "reset-gpios", 0,
171 &priv->reset_gpio, GPIOD_IS_OUT);
172 if (ret) {
173 log_err("could not decode reser-gpios (%d)\n", ret);
174 return ret;
175 }
Svyatoslav Ryhel8a13fd22024-01-31 08:57:21 +0200176
177 return 0;
178}
179
180static int renesas_r69328_hw_init(struct udevice *dev)
181{
182 struct renesas_r69328_priv *priv = dev_get_priv(dev);
183 int ret;
184
185 ret = dm_gpio_set_value(&priv->enable_gpio, 1);
186 if (ret) {
187 log_debug("%s: error changing enable-gpios (%d)\n",
188 __func__, ret);
189 return ret;
190 }
191 mdelay(5);
192
193 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
194 if (ret) {
195 log_debug("%s: error changing reset-gpios (%d)\n",
196 __func__, ret);
197 return ret;
198 }
199 mdelay(5);
200
201 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
202 if (ret) {
203 log_debug("%s: error changing reset-gpios (%d)\n",
204 __func__, ret);
205 return ret;
206 }
207
208 mdelay(5);
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300209
210 return 0;
211}
212
213static int renesas_r69328_probe(struct udevice *dev)
214{
215 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
216
217 /* fill characteristics of DSI data link */
218 plat->lanes = 4;
219 plat->format = MIPI_DSI_FMT_RGB888;
220 plat->mode_flags = MIPI_DSI_MODE_VIDEO;
221
Svyatoslav Ryhel8a13fd22024-01-31 08:57:21 +0200222 return renesas_r69328_hw_init(dev);
Svyatoslav Ryhelfcbbd562023-04-25 10:51:45 +0300223}
224
225static const struct panel_ops renesas_r69328_ops = {
226 .enable_backlight = renesas_r69328_enable_backlight,
227 .set_backlight = renesas_r69328_set_backlight,
228 .get_display_timing = renesas_r69328_timings,
229};
230
231static const struct udevice_id renesas_r69328_ids[] = {
232 { .compatible = "jdi,dx12d100vm0eaa" },
233 { }
234};
235
236U_BOOT_DRIVER(renesas_r69328) = {
237 .name = "renesas_r69328",
238 .id = UCLASS_PANEL,
239 .of_match = renesas_r69328_ids,
240 .ops = &renesas_r69328_ops,
241 .of_to_plat = renesas_r69328_of_to_plat,
242 .probe = renesas_r69328_probe,
243 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
244 .priv_auto = sizeof(struct renesas_r69328_priv),
245};