blob: 426fdc6224a043065a188a86f2cd9f8b688692cb [file] [log] [blame]
Svyatoslav Ryhelb9054982023-04-25 10:51:44 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Renesas R61307 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/*
22 * The datasheet is not publicly available, all values are
23 * taken from the downstream. If you have access to datasheets,
24 * corrections are welcome.
25 */
26
27#define R61307_MACP 0xB0 /* Manufacturer CMD Protect */
28
29#define R61307_INVERSION 0xC1
30#define R61307_GAMMA_SET_A 0xC8 /* Gamma Setting A */
31#define R61307_GAMMA_SET_B 0xC9 /* Gamma Setting B */
32#define R61307_GAMMA_SET_C 0xCA /* Gamma Setting C */
33#define R61307_CONTRAST_SET 0xCC
34
35struct renesas_r61307_priv {
36 struct udevice *vcc;
37 struct udevice *iovcc;
38
39 struct udevice *backlight;
40
41 struct gpio_desc reset_gpio;
42
43 bool dig_cont_adj;
44 bool inversion;
45 u32 gamma;
46};
47
48static const u8 macp_on[] = {
49 R61307_MACP, 0x03
50};
51
52static const u8 macp_off[] = {
53 R61307_MACP, 0x04
54};
55
56static const u8 address_mode[] = {
57 MIPI_DCS_SET_ADDRESS_MODE
58};
59
60static const u8 contrast_setting[] = {
61 R61307_CONTRAST_SET,
62 0xdc, 0xb4, 0xff
63};
64
65static const u8 column_inversion[] = {
66 R61307_INVERSION,
67 0x00, 0x50, 0x03, 0x22,
68 0x16, 0x06, 0x60, 0x11
69};
70
71static const u8 line_inversion[] = {
72 R61307_INVERSION,
73 0x00, 0x10, 0x03, 0x22,
74 0x16, 0x06, 0x60, 0x01
75};
76
77static const u8 gamma_setting[][25] = {
78 {},
79 {
80 R61307_GAMMA_SET_A,
81 0x00, 0x06, 0x0a, 0x0f,
82 0x14, 0x1f, 0x1f, 0x17,
83 0x12, 0x0c, 0x09, 0x06,
84 0x00, 0x06, 0x0a, 0x0f,
85 0x14, 0x1f, 0x1f, 0x17,
86 0x12, 0x0c, 0x09, 0x06
87 },
88 {
89 R61307_GAMMA_SET_A,
90 0x00, 0x05, 0x0b, 0x0f,
91 0x11, 0x1d, 0x20, 0x18,
92 0x18, 0x09, 0x07, 0x06,
93 0x00, 0x05, 0x0b, 0x0f,
94 0x11, 0x1d, 0x20, 0x18,
95 0x18, 0x09, 0x07, 0x06
96 },
97 {
98 R61307_GAMMA_SET_A,
99 0x0b, 0x0d, 0x10, 0x14,
100 0x13, 0x1d, 0x20, 0x18,
101 0x12, 0x09, 0x07, 0x06,
102 0x0a, 0x0c, 0x10, 0x14,
103 0x13, 0x1d, 0x20, 0x18,
104 0x12, 0x09, 0x07, 0x06
105 },
106};
107
108static struct display_timing default_timing = {
109 .pixelclock.typ = 62000000,
110 .hactive.typ = 768,
111 .hfront_porch.typ = 116,
112 .hback_porch.typ = 81,
113 .hsync_len.typ = 5,
114 .vactive.typ = 1024,
115 .vfront_porch.typ = 24,
116 .vback_porch.typ = 8,
117 .vsync_len.typ = 2,
118};
119
120static int renesas_r61307_enable_backlight(struct udevice *dev)
121{
122 struct renesas_r61307_priv *priv = dev_get_priv(dev);
123 int ret;
124
125 ret = regulator_set_enable_if_allowed(priv->vcc, 1);
126 if (ret) {
127 log_err("enabling vcc-supply failed (%d)\n", ret);
128 return ret;
129 }
130 mdelay(5);
131
132 ret = regulator_set_enable_if_allowed(priv->iovcc, 1);
133 if (ret) {
134 log_err("enabling iovcc-supply failed (%d)\n", ret);
135 return ret;
136 }
137
138 ret = dm_gpio_set_value(&priv->reset_gpio, 0);
139 if (ret) {
140 log_err("changing reset-gpio failed (%d)\n", ret);
141 return ret;
142 }
143 mdelay(5);
144
145 ret = dm_gpio_set_value(&priv->reset_gpio, 1);
146 if (ret) {
147 log_err("changing reset-gpio failed (%d)\n", ret);
148 return ret;
149 }
150
151 mdelay(5);
152
153 return 0;
154}
155
156static int renesas_r61307_set_backlight(struct udevice *dev, int percent)
157{
158 struct renesas_r61307_priv *priv = dev_get_priv(dev);
159 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
160 struct mipi_dsi_device *dsi = plat->device;
161 int ret;
162
163 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
164 if (ret < 0) {
165 log_err("failed to exit sleep mode: %d\n", ret);
166 return ret;
167 }
168
169 mdelay(80);
170
171 mipi_dsi_dcs_write_buffer(dsi, address_mode,
172 sizeof(address_mode));
173
174 mdelay(20);
175
176 ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
177 if (ret < 0) {
178 log_err("failed to set pixel format: %d\n", ret);
179 return ret;
180 }
181
182 /* MACP Off */
183 mipi_dsi_generic_write(dsi, macp_off, sizeof(macp_off));
184
185 if (priv->dig_cont_adj)
186 mipi_dsi_generic_write(dsi, contrast_setting,
187 sizeof(contrast_setting));
188
189 if (priv->gamma)
190 mipi_dsi_generic_write(dsi, gamma_setting[priv->gamma],
191 sizeof(gamma_setting[priv->gamma]));
192
193 if (priv->inversion)
194 mipi_dsi_generic_write(dsi, column_inversion,
195 sizeof(column_inversion));
196 else
197 mipi_dsi_generic_write(dsi, line_inversion,
198 sizeof(line_inversion));
199
200 /* MACP On */
201 mipi_dsi_generic_write(dsi, macp_on, sizeof(macp_on));
202
203 ret = mipi_dsi_dcs_set_display_on(dsi);
204 if (ret < 0) {
205 log_err("failed to set display on: %d\n", ret);
206 return ret;
207 }
208
209 mdelay(50);
210
211 ret = backlight_enable(priv->backlight);
212 if (ret)
213 return ret;
214
215 ret = backlight_set_brightness(priv->backlight, percent);
216 if (ret)
217 return ret;
218
219 return 0;
220}
221
222static int renesas_r61307_timings(struct udevice *dev,
223 struct display_timing *timing)
224{
225 memcpy(timing, &default_timing, sizeof(*timing));
226 return 0;
227}
228
229static int renesas_r61307_of_to_plat(struct udevice *dev)
230{
231 struct renesas_r61307_priv *priv = dev_get_priv(dev);
232 int ret;
233
234 ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
235 "backlight", &priv->backlight);
236 if (ret) {
237 log_err("Cannot get backlight: ret = %d\n", ret);
238 return ret;
239 }
240
241 ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
242 "vcc-supply", &priv->vcc);
243 if (ret) {
244 log_err("Cannot get vcc-supply: ret = %d\n", ret);
245 return ret;
246 }
247
248 ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
249 "iovcc-supply", &priv->iovcc);
250 if (ret) {
251 log_err("Cannot get iovcc-supply: ret = %d\n", ret);
252 return ret;
253 }
254
255 ret = gpio_request_by_name(dev, "reset-gpios", 0,
256 &priv->reset_gpio, GPIOD_IS_OUT);
257 if (ret) {
258 log_err("Could not decode reser-gpios (%d)\n", ret);
259 return ret;
260 }
261
262 priv->dig_cont_adj = dev_read_bool(dev, "renesas,contrast");
263 priv->inversion = dev_read_bool(dev, "renesas,inversion");
264 priv->gamma = dev_read_u32_default(dev, "renesas,gamma", 0);
265
266 return 0;
267}
268
269static int renesas_r61307_probe(struct udevice *dev)
270{
271 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
272
273 /* fill characteristics of DSI data link */
274 plat->lanes = 4;
275 plat->format = MIPI_DSI_FMT_RGB888;
276 plat->mode_flags = MIPI_DSI_MODE_VIDEO;
277
278 return 0;
279}
280
281static const struct panel_ops renesas_r61307_ops = {
282 .enable_backlight = renesas_r61307_enable_backlight,
283 .set_backlight = renesas_r61307_set_backlight,
284 .get_display_timing = renesas_r61307_timings,
285};
286
287static const struct udevice_id renesas_r61307_ids[] = {
288 { .compatible = "koe,tx13d100vm0eaa" },
289 { .compatible = "hitachi,tx13d100vm0eaa" },
290 { }
291};
292
293U_BOOT_DRIVER(renesas_r61307) = {
294 .name = "renesas_r61307",
295 .id = UCLASS_PANEL,
296 .of_match = renesas_r61307_ids,
297 .ops = &renesas_r61307_ops,
298 .of_to_plat = renesas_r61307_of_to_plat,
299 .probe = renesas_r61307_probe,
300 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
301 .priv_auto = sizeof(struct renesas_r61307_priv),
302};