blob: f14f7642d0a28c9769e42aabcbe14666e64d6408 [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
8#include <common.h>
9#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{
68 struct renesas_r69328_priv *priv = dev_get_priv(dev);
69 int ret;
70
71 ret = dm_gpio_set_value(&priv->enable_gpio, 1);
72 if (ret) {
73 log_err("error changing enable-gpios (%d)\n", ret);
74 return ret;
75 }
76 mdelay(5);
77
78 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
79 if (ret) {
80 log_err("error changing reset-gpios (%d)\n", ret);
81 return ret;
82 }
83 mdelay(5);
84
85 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
86 if (ret) {
87 log_err("error changing reset-gpios (%d)\n", ret);
88 return ret;
89 }
90
91 mdelay(5);
92
93 return 0;
94}
95
96static int renesas_r69328_set_backlight(struct udevice *dev, int percent)
97{
98 struct renesas_r69328_priv *priv = dev_get_priv(dev);
99 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
100 struct mipi_dsi_device *dsi = plat->device;
101 int ret;
102
103 mipi_dsi_dcs_write_buffer(dsi, address_mode,
104 sizeof(address_mode));
105
106 ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
107 if (ret < 0) {
108 log_err("failed to set pixel format: %d\n", ret);
109 return ret;
110 }
111
112 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
113 if (ret < 0) {
114 log_err("failed to exit sleep mode: %d\n", ret);
115 return ret;
116 }
117
118 mdelay(100);
119
120 /* MACP Off */
121 dsi_generic_write_seq(dsi, R69328_MACP, 0x04);
122
123 dsi_generic_write_seq(dsi, R69328_POWER_SET, 0x14,
124 0x1d, 0x21, 0x67, 0x11, 0x9a);
125
126 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_A, 0x00,
127 0x1a, 0x20, 0x28, 0x25, 0x24,
128 0x26, 0x15, 0x13, 0x11, 0x18,
129 0x1e, 0x1c, 0x00, 0x00, 0x1a,
130 0x20, 0x28, 0x25, 0x24, 0x26,
131 0x15, 0x13, 0x11, 0x18, 0x1e,
132 0x1c, 0x00);
133 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_B, 0x00,
134 0x1a, 0x20, 0x28, 0x25, 0x24,
135 0x26, 0x15, 0x13, 0x11, 0x18,
136 0x1e, 0x1c, 0x00, 0x00, 0x1a,
137 0x20, 0x28, 0x25, 0x24, 0x26,
138 0x15, 0x13, 0x11, 0x18, 0x1e,
139 0x1c, 0x00);
140 dsi_generic_write_seq(dsi, R69328_GAMMA_SET_C, 0x00,
141 0x1a, 0x20, 0x28, 0x25, 0x24,
142 0x26, 0x15, 0x13, 0x11, 0x18,
143 0x1e, 0x1c, 0x00, 0x00, 0x1a,
144 0x20, 0x28, 0x25, 0x24, 0x26,
145 0x15, 0x13, 0x11, 0x18, 0x1e,
146 0x1c, 0x00);
147
148 /* MACP On */
149 dsi_generic_write_seq(dsi, R69328_MACP, 0x03);
150
151 ret = mipi_dsi_dcs_set_display_on(dsi);
152 if (ret < 0) {
153 log_err("failed to set display on: %d\n", ret);
154 return ret;
155 }
156
157 mdelay(50);
158
159 ret = backlight_enable(priv->backlight);
160 if (ret)
161 return ret;
162
163 ret = backlight_set_brightness(priv->backlight, percent);
164 if (ret)
165 return ret;
166
167 return 0;
168}
169
170static int renesas_r69328_timings(struct udevice *dev,
171 struct display_timing *timing)
172{
173 memcpy(timing, &default_timing, sizeof(*timing));
174 return 0;
175}
176
177static int renesas_r69328_of_to_plat(struct udevice *dev)
178{
179 struct renesas_r69328_priv *priv = dev_get_priv(dev);
180 int ret;
181
182 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
183 "backlight", &priv->backlight);
184 if (ret) {
185 log_err("cannot get backlight: ret = %d\n", ret);
186 return ret;
187 }
188
189 ret = gpio_request_by_name(dev, "enable-gpios", 0,
190 &priv->enable_gpio, GPIOD_IS_OUT);
191 if (ret) {
192 log_err("could not decode enable-gpios (%d)\n", ret);
193 return ret;
194 }
195
196 ret = gpio_request_by_name(dev, "reset-gpios", 0,
197 &priv->reset_gpio, GPIOD_IS_OUT);
198 if (ret) {
199 log_err("could not decode reser-gpios (%d)\n", ret);
200 return ret;
201 }
202
203 return 0;
204}
205
206static int renesas_r69328_probe(struct udevice *dev)
207{
208 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
209
210 /* fill characteristics of DSI data link */
211 plat->lanes = 4;
212 plat->format = MIPI_DSI_FMT_RGB888;
213 plat->mode_flags = MIPI_DSI_MODE_VIDEO;
214
215 return 0;
216}
217
218static const struct panel_ops renesas_r69328_ops = {
219 .enable_backlight = renesas_r69328_enable_backlight,
220 .set_backlight = renesas_r69328_set_backlight,
221 .get_display_timing = renesas_r69328_timings,
222};
223
224static const struct udevice_id renesas_r69328_ids[] = {
225 { .compatible = "jdi,dx12d100vm0eaa" },
226 { }
227};
228
229U_BOOT_DRIVER(renesas_r69328) = {
230 .name = "renesas_r69328",
231 .id = UCLASS_PANEL,
232 .of_match = renesas_r69328_ids,
233 .ops = &renesas_r69328_ops,
234 .of_to_plat = renesas_r69328_of_to_plat,
235 .probe = renesas_r69328_probe,
236 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
237 .priv_auto = sizeof(struct renesas_r69328_priv),
238};