1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/array_size.h>
4 #include <linux/delay.h>
5 #include <linux/err.h>
6 #include <linux/gpio/consumer.h>
7 #include <linux/mod_devicetable.h>
8 #include <linux/module.h>
9 #include <linux/property.h>
10 #include <linux/regulator/consumer.h>
11
12 #include <video/mipi_display.h>
13
14 #include <drm/drm_mipi_dsi.h>
15 #include <drm/drm_modes.h>
16 #include <drm/drm_panel.h>
17
18 #define R61307_MACP 0xb0 /* Manufacturer CMD Protect */
19 #define R61307_MACP_ON 0x03
20 #define R61307_MACP_OFF 0x04
21
22 #define R61307_INVERSION 0xc1
23 #define R61307_GAMMA_SET_A 0xc8 /* Gamma Setting A */
24 #define R61307_GAMMA_SET_B 0xc9 /* Gamma Setting B */
25 #define R61307_GAMMA_SET_C 0xca /* Gamma Setting C */
26 #define R61307_CONTRAST_SET 0xcc
27
28 struct renesas_r61307 {
29 struct drm_panel panel;
30 struct mipi_dsi_device *dsi;
31
32 struct regulator *vcc_supply;
33 struct regulator *iovcc_supply;
34
35 struct gpio_desc *reset_gpio;
36
37 bool prepared;
38
39 bool dig_cont_adj;
40 bool inversion;
41 u32 gamma;
42 };
43
44 static const u8 gamma_setting[][25] = {
45 { /* sentinel */ },
46 {
47 R61307_GAMMA_SET_A,
48 0x00, 0x06, 0x0a, 0x0f,
49 0x14, 0x1f, 0x1f, 0x17,
50 0x12, 0x0c, 0x09, 0x06,
51 0x00, 0x06, 0x0a, 0x0f,
52 0x14, 0x1f, 0x1f, 0x17,
53 0x12, 0x0c, 0x09, 0x06
54 },
55 {
56 R61307_GAMMA_SET_A,
57 0x00, 0x05, 0x0b, 0x0f,
58 0x11, 0x1d, 0x20, 0x18,
59 0x18, 0x09, 0x07, 0x06,
60 0x00, 0x05, 0x0b, 0x0f,
61 0x11, 0x1d, 0x20, 0x18,
62 0x18, 0x09, 0x07, 0x06
63 },
64 {
65 R61307_GAMMA_SET_A,
66 0x0b, 0x0d, 0x10, 0x14,
67 0x13, 0x1d, 0x20, 0x18,
68 0x12, 0x09, 0x07, 0x06,
69 0x0a, 0x0c, 0x10, 0x14,
70 0x13, 0x1d, 0x20, 0x18,
71 0x12, 0x09, 0x07, 0x06
72 },
73 };
74
to_renesas_r61307(struct drm_panel * panel)75 static inline struct renesas_r61307 *to_renesas_r61307(struct drm_panel *panel)
76 {
77 return container_of(panel, struct renesas_r61307, panel);
78 }
79
renesas_r61307_reset(struct renesas_r61307 * priv)80 static void renesas_r61307_reset(struct renesas_r61307 *priv)
81 {
82 gpiod_set_value_cansleep(priv->reset_gpio, 1);
83 usleep_range(10000, 11000);
84 gpiod_set_value_cansleep(priv->reset_gpio, 0);
85 usleep_range(2000, 3000);
86 }
87
renesas_r61307_prepare(struct drm_panel * panel)88 static int renesas_r61307_prepare(struct drm_panel *panel)
89 {
90 struct renesas_r61307 *priv = to_renesas_r61307(panel);
91 struct device *dev = &priv->dsi->dev;
92 int ret;
93
94 if (priv->prepared)
95 return 0;
96
97 ret = regulator_enable(priv->vcc_supply);
98 if (ret) {
99 dev_err(dev, "failed to enable vcc power supply\n");
100 return ret;
101 }
102
103 usleep_range(2000, 3000);
104
105 ret = regulator_enable(priv->iovcc_supply);
106 if (ret) {
107 dev_err(dev, "failed to enable iovcc power supply\n");
108 return ret;
109 }
110
111 usleep_range(2000, 3000);
112
113 renesas_r61307_reset(priv);
114
115 priv->prepared = true;
116 return 0;
117 }
118
renesas_r61307_enable(struct drm_panel * panel)119 static int renesas_r61307_enable(struct drm_panel *panel)
120 {
121 struct renesas_r61307 *priv = to_renesas_r61307(panel);
122 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
123
124 mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
125 mipi_dsi_msleep(&ctx, 80);
126
127 mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
128 mipi_dsi_msleep(&ctx, 20);
129
130 mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4);
131
132 /* MACP Off */
133 mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_OFF);
134
135 if (priv->dig_cont_adj)
136 mipi_dsi_generic_write_seq_multi(&ctx, R61307_CONTRAST_SET,
137 0xdc, 0xb4, 0xff);
138
139 if (priv->gamma)
140 mipi_dsi_generic_write_multi(&ctx, gamma_setting[priv->gamma],
141 sizeof(gamma_setting[priv->gamma]));
142
143 if (priv->inversion)
144 mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION,
145 0x00, 0x50, 0x03, 0x22,
146 0x16, 0x06, 0x60, 0x11);
147 else
148 mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION,
149 0x00, 0x10, 0x03, 0x22,
150 0x16, 0x06, 0x60, 0x01);
151
152 /* MACP On */
153 mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_ON);
154
155 mipi_dsi_dcs_set_display_on_multi(&ctx);
156 mipi_dsi_msleep(&ctx, 50);
157
158 return 0;
159 }
160
renesas_r61307_disable(struct drm_panel * panel)161 static int renesas_r61307_disable(struct drm_panel *panel)
162 {
163 struct renesas_r61307 *priv = to_renesas_r61307(panel);
164 struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
165
166 mipi_dsi_dcs_set_display_off_multi(&ctx);
167 mipi_dsi_msleep(&ctx, 100);
168 mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
169
170 return 0;
171 }
172
renesas_r61307_unprepare(struct drm_panel * panel)173 static int renesas_r61307_unprepare(struct drm_panel *panel)
174 {
175 struct renesas_r61307 *priv = to_renesas_r61307(panel);
176
177 if (!priv->prepared)
178 return 0;
179
180 usleep_range(10000, 11000);
181
182 gpiod_set_value_cansleep(priv->reset_gpio, 1);
183 usleep_range(5000, 6000);
184
185 regulator_disable(priv->iovcc_supply);
186 usleep_range(2000, 3000);
187 regulator_disable(priv->vcc_supply);
188
189 priv->prepared = false;
190 return 0;
191 }
192
193 static const struct drm_display_mode renesas_r61307_mode = {
194 .clock = (768 + 116 + 81 + 5) * (1024 + 24 + 8 + 2) * 60 / 1000,
195 .hdisplay = 768,
196 .hsync_start = 768 + 116,
197 .hsync_end = 768 + 116 + 81,
198 .htotal = 768 + 116 + 81 + 5,
199 .vdisplay = 1024,
200 .vsync_start = 1024 + 24,
201 .vsync_end = 1024 + 24 + 8,
202 .vtotal = 1024 + 24 + 8 + 2,
203 .width_mm = 76,
204 .height_mm = 101,
205 };
206
renesas_r61307_get_modes(struct drm_panel * panel,struct drm_connector * connector)207 static int renesas_r61307_get_modes(struct drm_panel *panel,
208 struct drm_connector *connector)
209 {
210 struct drm_display_mode *mode;
211
212 mode = drm_mode_duplicate(connector->dev, &renesas_r61307_mode);
213 if (!mode)
214 return -ENOMEM;
215
216 drm_mode_set_name(mode);
217
218 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
219 connector->display_info.width_mm = mode->width_mm;
220 connector->display_info.height_mm = mode->height_mm;
221 drm_mode_probed_add(connector, mode);
222
223 return 1;
224 }
225
226 static const struct drm_panel_funcs renesas_r61307_panel_funcs = {
227 .prepare = renesas_r61307_prepare,
228 .enable = renesas_r61307_enable,
229 .disable = renesas_r61307_disable,
230 .unprepare = renesas_r61307_unprepare,
231 .get_modes = renesas_r61307_get_modes,
232 };
233
renesas_r61307_probe(struct mipi_dsi_device * dsi)234 static int renesas_r61307_probe(struct mipi_dsi_device *dsi)
235 {
236 struct device *dev = &dsi->dev;
237 struct renesas_r61307 *priv;
238 int ret;
239
240 priv = devm_drm_panel_alloc(dev, struct renesas_r61307, panel,
241 &renesas_r61307_panel_funcs,
242 DRM_MODE_CONNECTOR_DSI);
243 if (IS_ERR(priv))
244 return PTR_ERR(priv);
245
246 priv->vcc_supply = devm_regulator_get(dev, "vcc");
247 if (IS_ERR(priv->vcc_supply))
248 return dev_err_probe(dev, PTR_ERR(priv->vcc_supply),
249 "Failed to get vcc-supply\n");
250
251 priv->iovcc_supply = devm_regulator_get(dev, "iovcc");
252 if (IS_ERR(priv->iovcc_supply))
253 return dev_err_probe(dev, PTR_ERR(priv->iovcc_supply),
254 "Failed to get iovcc-supply\n");
255
256 priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
257 GPIOD_OUT_HIGH);
258 if (IS_ERR(priv->reset_gpio))
259 return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
260 "Failed to get reset gpios\n");
261
262 if (device_property_read_bool(dev, "renesas,inversion"))
263 priv->inversion = true;
264
265 if (device_property_read_bool(dev, "renesas,contrast"))
266 priv->dig_cont_adj = true;
267
268 priv->gamma = 0;
269 device_property_read_u32(dev, "renesas,gamma", &priv->gamma);
270
271 priv->dsi = dsi;
272 mipi_dsi_set_drvdata(dsi, priv);
273
274 dsi->lanes = 4;
275 dsi->format = MIPI_DSI_FMT_RGB888;
276 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
277 MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
278
279 ret = drm_panel_of_backlight(&priv->panel);
280 if (ret)
281 return dev_err_probe(dev, ret, "Failed to get backlight\n");
282
283 drm_panel_add(&priv->panel);
284
285 ret = mipi_dsi_attach(dsi);
286 if (ret) {
287 drm_panel_remove(&priv->panel);
288 return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
289 }
290
291 return 0;
292 }
293
renesas_r61307_remove(struct mipi_dsi_device * dsi)294 static void renesas_r61307_remove(struct mipi_dsi_device *dsi)
295 {
296 struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi);
297 int ret;
298
299 ret = mipi_dsi_detach(dsi);
300 if (ret)
301 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
302
303 drm_panel_remove(&priv->panel);
304 }
305
306 static const struct of_device_id renesas_r61307_of_match[] = {
307 { .compatible = "hit,tx13d100vm0eaa" },
308 { .compatible = "koe,tx13d100vm0eaa" },
309 { /* sentinel */ }
310 };
311 MODULE_DEVICE_TABLE(of, renesas_r61307_of_match);
312
313 static struct mipi_dsi_driver renesas_r61307_driver = {
314 .probe = renesas_r61307_probe,
315 .remove = renesas_r61307_remove,
316 .driver = {
317 .name = "panel-renesas-r61307",
318 .of_match_table = renesas_r61307_of_match,
319 },
320 };
321 module_mipi_dsi_driver(renesas_r61307_driver);
322
323 MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
324 MODULE_DESCRIPTION("Renesas R61307-based panel driver");
325 MODULE_LICENSE("GPL");
326