blob: 8416899266cf71dcd0ade18d7f3c68b0559b042d [file] [log] [blame]
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001/*
2 * Copyright (C) 2015 Free Electrons
3 * Copyright (C) 2015 NextThing Co
4 *
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 */
12
13#include <drm/drmP.h>
14#include <drm/drm_atomic_helper.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +080017#include <drm/drm_encoder.h>
Maxime Ripard9026e0d2015-10-29 09:36:23 +010018#include <drm/drm_modes.h>
Rob Herringebc94462017-03-29 13:55:46 -050019#include <drm/drm_of.h>
Maxime Ripard9026e0d2015-10-29 09:36:23 +010020
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +080021#include <uapi/drm/drm_mode.h>
22
Maxime Ripard9026e0d2015-10-29 09:36:23 +010023#include <linux/component.h>
24#include <linux/ioport.h>
25#include <linux/of_address.h>
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +080026#include <linux/of_device.h>
Maxime Ripard9026e0d2015-10-29 09:36:23 +010027#include <linux/of_irq.h>
28#include <linux/regmap.h>
29#include <linux/reset.h>
30
31#include "sun4i_crtc.h"
32#include "sun4i_dotclock.h"
33#include "sun4i_drv.h"
Maxime Riparda0c12142017-12-21 12:02:33 +010034#include "sun4i_lvds.h"
Maxime Ripard29e57fa2015-10-29 09:37:32 +010035#include "sun4i_rgb.h"
Maxime Ripard9026e0d2015-10-29 09:36:23 +010036#include "sun4i_tcon.h"
Icenowy Zheng87969332017-05-17 22:47:17 +080037#include "sunxi_engine.h"
Maxime Ripard9026e0d2015-10-29 09:36:23 +010038
Maxime Riparda0c12142017-12-21 12:02:33 +010039static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
40{
41 struct drm_connector *connector;
42 struct drm_connector_list_iter iter;
43
44 drm_connector_list_iter_begin(encoder->dev, &iter);
45 drm_for_each_connector_iter(connector, &iter)
46 if (connector->encoder == encoder) {
47 drm_connector_list_iter_end(&iter);
48 return connector;
49 }
50 drm_connector_list_iter_end(&iter);
51
52 return NULL;
53}
54
55static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder)
56{
57 struct drm_connector *connector;
58 struct drm_display_info *info;
59
60 connector = sun4i_tcon_get_connector(encoder);
61 if (!connector)
62 return -EINVAL;
63
64 info = &connector->display_info;
65 if (info->num_bus_formats != 1)
66 return -EINVAL;
67
68 switch (info->bus_formats[0]) {
69 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
70 return 18;
71
72 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
73 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
74 return 24;
75 }
76
77 return -EINVAL;
78}
79
Maxime Ripard45e88f92017-10-17 11:06:12 +020080static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
81 bool enabled)
Maxime Ripard9026e0d2015-10-29 09:36:23 +010082{
Maxime Ripard45e88f92017-10-17 11:06:12 +020083 struct clk *clk;
Maxime Ripard9026e0d2015-10-29 09:36:23 +010084
Maxime Ripard45e88f92017-10-17 11:06:12 +020085 switch (channel) {
86 case 0:
Jernej Skrabec34d698f2018-02-14 21:09:01 +010087 WARN_ON(!tcon->quirks->has_channel_0);
Maxime Ripard9026e0d2015-10-29 09:36:23 +010088 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
89 SUN4I_TCON0_CTL_TCON_ENABLE,
Maxime Ripard45e88f92017-10-17 11:06:12 +020090 enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);
91 clk = tcon->dclk;
92 break;
93 case 1:
94 WARN_ON(!tcon->quirks->has_channel_1);
95 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
96 SUN4I_TCON1_CTL_TCON_ENABLE,
97 enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0);
98 clk = tcon->sclk1;
99 break;
100 default:
101 DRM_WARN("Unknown channel... doing nothing\n");
Maxime Ripard8e924042016-01-07 12:32:07 +0100102 return;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100103 }
Maxime Ripard8e924042016-01-07 12:32:07 +0100104
Maxime Ripard45e88f92017-10-17 11:06:12 +0200105 if (enabled)
106 clk_prepare_enable(clk);
107 else
108 clk_disable_unprepare(clk);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100109}
Maxime Ripard45e88f92017-10-17 11:06:12 +0200110
Maxime Riparda0c12142017-12-21 12:02:33 +0100111static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,
112 const struct drm_encoder *encoder,
113 bool enabled)
114{
115 if (enabled) {
116 u8 val;
117
118 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
119 SUN4I_TCON0_LVDS_IF_EN,
120 SUN4I_TCON0_LVDS_IF_EN);
121
122 /*
123 * As their name suggest, these values only apply to the A31
124 * and later SoCs. We'll have to rework this when merging
125 * support for the older SoCs.
126 */
127 regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
128 SUN6I_TCON0_LVDS_ANA0_C(2) |
129 SUN6I_TCON0_LVDS_ANA0_V(3) |
130 SUN6I_TCON0_LVDS_ANA0_PD(2) |
131 SUN6I_TCON0_LVDS_ANA0_EN_LDO);
132 udelay(2);
133
134 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
135 SUN6I_TCON0_LVDS_ANA0_EN_MB,
136 SUN6I_TCON0_LVDS_ANA0_EN_MB);
137 udelay(2);
138
139 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
140 SUN6I_TCON0_LVDS_ANA0_EN_DRVC,
141 SUN6I_TCON0_LVDS_ANA0_EN_DRVC);
142
143 if (sun4i_tcon_get_pixel_depth(encoder) == 18)
144 val = 7;
145 else
146 val = 0xf;
147
148 regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,
149 SUN6I_TCON0_LVDS_ANA0_EN_DRVD(0xf),
150 SUN6I_TCON0_LVDS_ANA0_EN_DRVD(val));
151 } else {
152 regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,
153 SUN4I_TCON0_LVDS_IF_EN, 0);
154 }
155}
156
Maxime Ripard45e88f92017-10-17 11:06:12 +0200157void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
158 const struct drm_encoder *encoder,
159 bool enabled)
160{
Maxime Riparda0c12142017-12-21 12:02:33 +0100161 bool is_lvds = false;
Maxime Ripard45e88f92017-10-17 11:06:12 +0200162 int channel;
163
164 switch (encoder->encoder_type) {
Maxime Riparda0c12142017-12-21 12:02:33 +0100165 case DRM_MODE_ENCODER_LVDS:
166 is_lvds = true;
167 /* Fallthrough */
Maxime Ripard45e88f92017-10-17 11:06:12 +0200168 case DRM_MODE_ENCODER_NONE:
169 channel = 0;
170 break;
171 case DRM_MODE_ENCODER_TMDS:
172 case DRM_MODE_ENCODER_TVDAC:
173 channel = 1;
174 break;
175 default:
176 DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
177 return;
178 }
179
Maxime Riparda0c12142017-12-21 12:02:33 +0100180 if (is_lvds && !enabled)
181 sun4i_tcon_lvds_set_status(tcon, encoder, false);
182
Maxime Ripard45e88f92017-10-17 11:06:12 +0200183 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
184 SUN4I_TCON_GCTL_TCON_ENABLE,
185 enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
186
Maxime Riparda0c12142017-12-21 12:02:33 +0100187 if (is_lvds && enabled)
188 sun4i_tcon_lvds_set_status(tcon, encoder, true);
189
Maxime Ripard45e88f92017-10-17 11:06:12 +0200190 sun4i_tcon_channel_set_status(tcon, channel, enabled);
191}
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100192
193void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
194{
195 u32 mask, val = 0;
196
197 DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");
198
199 mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |
200 SUN4I_TCON_GINT0_VBLANK_ENABLE(1);
201
202 if (enable)
203 val = mask;
204
205 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val);
206}
207EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
208
Chen-Yu Tsai67e32642017-10-10 11:19:59 +0800209/*
210 * This function is a helper for TCON output muxing. The TCON output
211 * muxing control register in earlier SoCs (without the TCON TOP block)
212 * are located in TCON0. This helper returns a pointer to TCON0's
213 * sun4i_tcon structure, or NULL if not found.
214 */
215static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
216{
217 struct sun4i_drv *drv = drm->dev_private;
218 struct sun4i_tcon *tcon;
219
220 list_for_each_entry(tcon, &drv->tcon_list, list)
221 if (tcon->id == 0)
222 return tcon;
223
224 dev_warn(drm->dev,
225 "TCON0 not found, display output muxing may not work\n");
226
227 return NULL;
228}
229
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200230void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
Maxime Ripardabcb8762017-10-17 11:06:10 +0200231 const struct drm_encoder *encoder)
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200232{
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800233 int ret = -ENOTSUPP;
Maxime Ripardb7cb9b92017-05-27 18:09:28 +0200234
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800235 if (tcon->quirks->set_mux)
236 ret = tcon->quirks->set_mux(tcon, encoder);
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200237
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800238 DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",
239 encoder->name, encoder->crtc->name, ret);
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200240}
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200241
Maxime Ripard961c6452017-10-17 11:06:11 +0200242static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode,
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100243 int channel)
244{
245 int delay = mode->vtotal - mode->vdisplay;
246
247 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
248 delay /= 2;
249
250 if (channel == 1)
251 delay -= 2;
252
253 delay = min(delay, 30);
254
255 DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay);
256
257 return delay;
258}
259
Maxime Ripardba19c532017-10-17 11:06:14 +0200260static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
261 const struct drm_display_mode *mode)
262{
263 /* Configure the dot clock */
264 clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
265
266 /* Set the resolution */
267 regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,
268 SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |
269 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
270}
271
Maxime Riparda0c12142017-12-21 12:02:33 +0100272static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
273 const struct drm_encoder *encoder,
274 const struct drm_display_mode *mode)
275{
276 unsigned int bp;
277 u8 clk_delay;
278 u32 reg, val = 0;
279
Jernej Skrabec34d698f2018-02-14 21:09:01 +0100280 WARN_ON(!tcon->quirks->has_channel_0);
281
Maxime Riparda0c12142017-12-21 12:02:33 +0100282 tcon->dclk_min_div = 7;
283 tcon->dclk_max_div = 7;
284 sun4i_tcon0_mode_set_common(tcon, mode);
285
286 /* Adjust clock delay */
287 clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
288 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
289 SUN4I_TCON0_CTL_CLK_DELAY_MASK,
290 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
291
292 /*
293 * This is called a backporch in the register documentation,
294 * but it really is the back porch + hsync
295 */
296 bp = mode->crtc_htotal - mode->crtc_hsync_start;
297 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
298 mode->crtc_htotal, bp);
299
300 /* Set horizontal display timings */
301 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
302 SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) |
303 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
304
305 /*
306 * This is called a backporch in the register documentation,
307 * but it really is the back porch + hsync
308 */
309 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
310 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
311 mode->crtc_vtotal, bp);
312
313 /* Set vertical display timings */
314 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
315 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
316 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
317
318 reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0 |
319 SUN4I_TCON0_LVDS_IF_DATA_POL_NORMAL |
320 SUN4I_TCON0_LVDS_IF_CLK_POL_NORMAL;
321 if (sun4i_tcon_get_pixel_depth(encoder) == 24)
322 reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;
323 else
324 reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS;
325
326 regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg);
327
328 /* Setup the polarity of the various signals */
329 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
330 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
331
332 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
333 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
334
335 regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);
336
337 /* Map output pins to channel 0 */
338 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
339 SUN4I_TCON_GCTL_IOMAP_MASK,
340 SUN4I_TCON_GCTL_IOMAP_TCON0);
341}
342
Maxime Ripardba19c532017-10-17 11:06:14 +0200343static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
344 const struct drm_display_mode *mode)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100345{
346 unsigned int bp, hsync, vsync;
347 u8 clk_delay;
348 u32 val = 0;
349
Jernej Skrabec34d698f2018-02-14 21:09:01 +0100350 WARN_ON(!tcon->quirks->has_channel_0);
351
Maxime Ripardec08d592017-12-21 12:02:32 +0100352 tcon->dclk_min_div = 6;
353 tcon->dclk_max_div = 127;
Maxime Ripardba19c532017-10-17 11:06:14 +0200354 sun4i_tcon0_mode_set_common(tcon, mode);
Chen-Yu Tsai86cf6782017-04-25 23:25:04 +0800355
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100356 /* Adjust clock delay */
357 clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
358 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
359 SUN4I_TCON0_CTL_CLK_DELAY_MASK,
360 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
361
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100362 /*
363 * This is called a backporch in the register documentation,
Chen-Yu Tsai23a1cb12017-03-09 18:05:25 +0800364 * but it really is the back porch + hsync
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100365 */
366 bp = mode->crtc_htotal - mode->crtc_hsync_start;
367 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
368 mode->crtc_htotal, bp);
369
370 /* Set horizontal display timings */
371 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
372 SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) |
373 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
374
375 /*
376 * This is called a backporch in the register documentation,
Chen-Yu Tsai23a1cb12017-03-09 18:05:25 +0800377 * but it really is the back porch + hsync
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100378 */
379 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
380 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
381 mode->crtc_vtotal, bp);
382
383 /* Set vertical display timings */
384 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
Maxime Riparda88cbbd2017-05-27 18:09:30 +0200385 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100386 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
387
388 /* Set Hsync and Vsync length */
389 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
390 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
391 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
392 regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG,
393 SUN4I_TCON0_BASIC3_V_SYNC(vsync) |
394 SUN4I_TCON0_BASIC3_H_SYNC(hsync));
395
396 /* Setup the polarity of the various signals */
Giulio Benettifa4127c2018-02-15 18:54:48 +0100397 if (mode->flags & DRM_MODE_FLAG_PHSYNC)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100398 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
399
Giulio Benettifa4127c2018-02-15 18:54:48 +0100400 if (mode->flags & DRM_MODE_FLAG_PVSYNC)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100401 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
402
403 regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG,
404 SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE,
405 val);
406
407 /* Map output pins to channel 0 */
408 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
409 SUN4I_TCON_GCTL_IOMAP_MASK,
410 SUN4I_TCON_GCTL_IOMAP_TCON0);
411
412 /* Enable the output on the pins */
413 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0);
414}
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100415
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200416static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
417 const struct drm_display_mode *mode)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100418{
Maxime Ripardb8317a32017-05-27 18:09:31 +0200419 unsigned int bp, hsync, vsync, vtotal;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100420 u8 clk_delay;
421 u32 val;
422
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800423 WARN_ON(!tcon->quirks->has_channel_1);
Maxime Ripard8e924042016-01-07 12:32:07 +0100424
Chen-Yu Tsai86cf6782017-04-25 23:25:04 +0800425 /* Configure the dot clock */
426 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
427
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100428 /* Adjust clock delay */
429 clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
430 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
431 SUN4I_TCON1_CTL_CLK_DELAY_MASK,
432 SUN4I_TCON1_CTL_CLK_DELAY(clk_delay));
433
434 /* Set interlaced mode */
435 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
436 val = SUN4I_TCON1_CTL_INTERLACE_ENABLE;
437 else
438 val = 0;
439 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
440 SUN4I_TCON1_CTL_INTERLACE_ENABLE,
441 val);
442
443 /* Set the input resolution */
444 regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,
445 SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |
446 SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));
447
448 /* Set the upscaling resolution */
449 regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,
450 SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |
451 SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));
452
453 /* Set the output resolution */
454 regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,
455 SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |
456 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
457
458 /* Set horizontal display timings */
Maxime Ripard3cb2f462017-05-27 18:09:29 +0200459 bp = mode->crtc_htotal - mode->crtc_hsync_start;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100460 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
461 mode->htotal, bp);
462 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
463 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
464 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
465
Maxime Ripard3cb2f462017-05-27 18:09:29 +0200466 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100467 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
Maxime Ripardb8317a32017-05-27 18:09:31 +0200468 mode->crtc_vtotal, bp);
469
470 /*
471 * The vertical resolution needs to be doubled in all
472 * cases. We could use crtc_vtotal and always multiply by two,
473 * but that leads to a rounding error in interlace when vtotal
474 * is odd.
475 *
476 * This happens with TV's PAL for example, where vtotal will
477 * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be
478 * 624, which apparently confuses the hardware.
479 *
480 * To work around this, we will always use vtotal, and
481 * multiply by two only if we're not in interlace.
482 */
483 vtotal = mode->vtotal;
484 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
485 vtotal = vtotal * 2;
486
487 /* Set vertical display timings */
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100488 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
Maxime Ripardb8317a32017-05-27 18:09:31 +0200489 SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) |
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100490 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
491
492 /* Set Hsync and Vsync length */
493 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
494 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
495 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
496 regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG,
497 SUN4I_TCON1_BASIC5_V_SYNC(vsync) |
498 SUN4I_TCON1_BASIC5_H_SYNC(hsync));
499
500 /* Map output pins to channel 1 */
501 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
502 SUN4I_TCON_GCTL_IOMAP_MASK,
503 SUN4I_TCON_GCTL_IOMAP_TCON1);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100504}
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200505
506void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
507 const struct drm_encoder *encoder,
508 const struct drm_display_mode *mode)
509{
510 switch (encoder->encoder_type) {
Maxime Riparda0c12142017-12-21 12:02:33 +0100511 case DRM_MODE_ENCODER_LVDS:
512 sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
513 break;
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200514 case DRM_MODE_ENCODER_NONE:
Maxime Ripardba19c532017-10-17 11:06:14 +0200515 sun4i_tcon0_mode_set_rgb(tcon, mode);
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200516 sun4i_tcon_set_mux(tcon, 0, encoder);
517 break;
518 case DRM_MODE_ENCODER_TVDAC:
519 case DRM_MODE_ENCODER_TMDS:
520 sun4i_tcon1_mode_set(tcon, mode);
521 sun4i_tcon_set_mux(tcon, 1, encoder);
522 break;
523 default:
524 DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
525 }
526}
527EXPORT_SYMBOL(sun4i_tcon_mode_set);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100528
529static void sun4i_tcon_finish_page_flip(struct drm_device *dev,
530 struct sun4i_crtc *scrtc)
531{
532 unsigned long flags;
533
534 spin_lock_irqsave(&dev->event_lock, flags);
535 if (scrtc->event) {
536 drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event);
537 drm_crtc_vblank_put(&scrtc->crtc);
538 scrtc->event = NULL;
539 }
540 spin_unlock_irqrestore(&dev->event_lock, flags);
541}
542
543static irqreturn_t sun4i_tcon_handler(int irq, void *private)
544{
545 struct sun4i_tcon *tcon = private;
546 struct drm_device *drm = tcon->drm;
Chen-Yu Tsai46cce6d2017-02-23 16:05:37 +0800547 struct sun4i_crtc *scrtc = tcon->crtc;
Maxime Ripard3004f752018-01-22 10:25:20 +0100548 struct sunxi_engine *engine = scrtc->engine;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100549 unsigned int status;
550
551 regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
552
553 if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
554 SUN4I_TCON_GINT0_VBLANK_INT(1))))
555 return IRQ_NONE;
556
557 drm_crtc_handle_vblank(&scrtc->crtc);
558 sun4i_tcon_finish_page_flip(drm, scrtc);
559
560 /* Acknowledge the interrupt */
561 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,
562 SUN4I_TCON_GINT0_VBLANK_INT(0) |
563 SUN4I_TCON_GINT0_VBLANK_INT(1),
564 0);
565
Maxime Ripard3004f752018-01-22 10:25:20 +0100566 if (engine->ops->vblank_quirk)
567 engine->ops->vblank_quirk(engine);
568
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100569 return IRQ_HANDLED;
570}
571
572static int sun4i_tcon_init_clocks(struct device *dev,
573 struct sun4i_tcon *tcon)
574{
575 tcon->clk = devm_clk_get(dev, "ahb");
576 if (IS_ERR(tcon->clk)) {
577 dev_err(dev, "Couldn't get the TCON bus clock\n");
578 return PTR_ERR(tcon->clk);
579 }
580 clk_prepare_enable(tcon->clk);
581
Jernej Skrabec34d698f2018-02-14 21:09:01 +0100582 if (tcon->quirks->has_channel_0) {
583 tcon->sclk0 = devm_clk_get(dev, "tcon-ch0");
584 if (IS_ERR(tcon->sclk0)) {
585 dev_err(dev, "Couldn't get the TCON channel 0 clock\n");
586 return PTR_ERR(tcon->sclk0);
587 }
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100588 }
589
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800590 if (tcon->quirks->has_channel_1) {
Maxime Ripard8e924042016-01-07 12:32:07 +0100591 tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
592 if (IS_ERR(tcon->sclk1)) {
593 dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
594 return PTR_ERR(tcon->sclk1);
595 }
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100596 }
597
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800598 return 0;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100599}
600
601static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
602{
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100603 clk_disable_unprepare(tcon->clk);
604}
605
606static int sun4i_tcon_init_irq(struct device *dev,
607 struct sun4i_tcon *tcon)
608{
609 struct platform_device *pdev = to_platform_device(dev);
610 int irq, ret;
611
612 irq = platform_get_irq(pdev, 0);
613 if (irq < 0) {
614 dev_err(dev, "Couldn't retrieve the TCON interrupt\n");
615 return irq;
616 }
617
618 ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0,
619 dev_name(dev), tcon);
620 if (ret) {
621 dev_err(dev, "Couldn't request the IRQ\n");
622 return ret;
623 }
624
625 return 0;
626}
627
628static struct regmap_config sun4i_tcon_regmap_config = {
629 .reg_bits = 32,
630 .val_bits = 32,
631 .reg_stride = 4,
632 .max_register = 0x800,
633};
634
635static int sun4i_tcon_init_regmap(struct device *dev,
636 struct sun4i_tcon *tcon)
637{
638 struct platform_device *pdev = to_platform_device(dev);
639 struct resource *res;
640 void __iomem *regs;
641
642 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
643 regs = devm_ioremap_resource(dev, res);
Wei Yongjunaf346f52016-08-26 14:25:25 +0000644 if (IS_ERR(regs))
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100645 return PTR_ERR(regs);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100646
647 tcon->regs = devm_regmap_init_mmio(dev, regs,
648 &sun4i_tcon_regmap_config);
649 if (IS_ERR(tcon->regs)) {
650 dev_err(dev, "Couldn't create the TCON regmap\n");
651 return PTR_ERR(tcon->regs);
652 }
653
654 /* Make sure the TCON is disabled and all IRQs are off */
655 regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);
656 regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
657 regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0);
658
659 /* Disable IO lines and set them to tristate */
660 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);
661 regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);
662
663 return 0;
664}
665
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800666/*
667 * On SoCs with the old display pipeline design (Display Engine 1.0),
668 * the TCON is always tied to just one backend. Hence we can traverse
669 * the of_graph upwards to find the backend our tcon is connected to,
670 * and take its ID as our own.
671 *
672 * We can either identify backends from their compatible strings, which
673 * means maintaining a large list of them. Or, since the backend is
674 * registered and binded before the TCON, we can just go through the
675 * list of registered backends and compare the device node.
Icenowy Zheng87969332017-05-17 22:47:17 +0800676 *
677 * As the structures now store engines instead of backends, here this
678 * function in fact searches the corresponding engine, and the ID is
679 * requested via the get_id function of the engine.
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800680 */
Chen-Yu Tsaie8d5bbf2017-09-08 15:50:12 +0800681static struct sunxi_engine *
682sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
683 struct device_node *node)
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800684{
685 struct device_node *port, *ep, *remote;
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800686 struct sunxi_engine *engine = ERR_PTR(-EINVAL);
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800687
688 port = of_graph_get_port_by_id(node, 0);
689 if (!port)
690 return ERR_PTR(-EINVAL);
691
Chen-Yu Tsai14696192017-09-08 15:50:11 +0800692 /*
693 * This only works if there is only one path from the TCON
694 * to any display engine. Otherwise the probe order of the
695 * TCONs and display engines is not guaranteed. They may
696 * either bind to the wrong one, or worse, bind to the same
697 * one if additional checks are not done.
698 *
699 * Bail out if there are multiple input connections.
700 */
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800701 if (of_get_available_child_count(port) != 1)
702 goto out_put_port;
Chen-Yu Tsai14696192017-09-08 15:50:11 +0800703
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800704 /* Get the first connection without specifying an ID */
705 ep = of_get_next_available_child(port, NULL);
706 if (!ep)
707 goto out_put_port;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800708
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800709 remote = of_graph_get_remote_port_parent(ep);
710 if (!remote)
711 goto out_put_ep;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800712
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800713 /* does this node match any registered engines? */
714 list_for_each_entry(engine, &drv->engine_list, list)
715 if (remote == engine->node)
716 goto out_put_remote;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800717
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800718 /* keep looking through upstream ports */
719 engine = sun4i_tcon_find_engine_traverse(drv, remote);
720
721out_put_remote:
722 of_node_put(remote);
723out_put_ep:
724 of_node_put(ep);
725out_put_port:
726 of_node_put(port);
727
728 return engine;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800729}
730
Chen-Yu Tsaie8d5bbf2017-09-08 15:50:12 +0800731/*
732 * The device tree binding says that the remote endpoint ID of any
733 * connection between components, up to and including the TCON, of
734 * the display pipeline should be equal to the actual ID of the local
735 * component. Thus we can look at any one of the input connections of
736 * the TCONs, and use that connection's remote endpoint ID as our own.
737 *
738 * Since the user of this function already finds the input port,
739 * the port is passed in directly without further checks.
740 */
741static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
742{
743 struct device_node *ep;
744 int ret = -EINVAL;
745
746 /* try finding an upstream endpoint */
747 for_each_available_child_of_node(port, ep) {
748 struct device_node *remote;
749 u32 reg;
750
751 remote = of_graph_get_remote_endpoint(ep);
752 if (!remote)
753 continue;
754
755 ret = of_property_read_u32(remote, "reg", &reg);
756 if (ret)
757 continue;
758
759 ret = reg;
760 }
761
762 return ret;
763}
764
765/*
766 * Once we know the TCON's id, we can look through the list of
767 * engines to find a matching one. We assume all engines have
768 * been probed and added to the list.
769 */
770static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,
771 int id)
772{
773 struct sunxi_engine *engine;
774
775 list_for_each_entry(engine, &drv->engine_list, list)
776 if (engine->id == id)
777 return engine;
778
779 return ERR_PTR(-EINVAL);
780}
781
782/*
783 * On SoCs with the old display pipeline design (Display Engine 1.0),
784 * we assumed the TCON was always tied to just one backend. However
785 * this proved not to be the case. On the A31, the TCON can select
786 * either backend as its source. On the A20 (and likely on the A10),
787 * the backend can choose which TCON to output to.
788 *
789 * The device tree binding says that the remote endpoint ID of any
790 * connection between components, up to and including the TCON, of
791 * the display pipeline should be equal to the actual ID of the local
792 * component. Thus we should be able to look at any one of the input
793 * connections of the TCONs, and use that connection's remote endpoint
794 * ID as our own.
795 *
796 * However the connections between the backend and TCON were assumed
797 * to be always singular, and their endpoit IDs were all incorrectly
798 * set to 0. This means for these old device trees, we cannot just look
799 * up the remote endpoint ID of a TCON input endpoint. TCON1 would be
800 * incorrectly identified as TCON0.
801 *
802 * This function first checks if the TCON node has 2 input endpoints.
803 * If so, then the device tree is a corrected version, and it will use
804 * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above
805 * to fetch the ID and engine directly. If not, then it is likely an
806 * old device trees, where the endpoint IDs were incorrect, but did not
807 * have endpoint connections between the backend and TCON across
808 * different display pipelines. It will fall back to the old method of
809 * traversing the of_graph to try and find a matching engine by device
810 * node.
811 *
812 * In the case of single display pipeline device trees, either method
813 * works.
814 */
815static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
816 struct device_node *node)
817{
818 struct device_node *port;
819 struct sunxi_engine *engine;
820
821 port = of_graph_get_port_by_id(node, 0);
822 if (!port)
823 return ERR_PTR(-EINVAL);
824
825 /*
826 * Is this a corrected device tree with cross pipeline
827 * connections between the backend and TCON?
828 */
829 if (of_get_child_count(port) > 1) {
830 /* Get our ID directly from an upstream endpoint */
831 int id = sun4i_tcon_of_get_id_from_port(port);
832
833 /* Get our engine by matching our ID */
834 engine = sun4i_tcon_get_engine_by_id(drv, id);
835
836 of_node_put(port);
837 return engine;
838 }
839
840 /* Fallback to old method by traversing input endpoints */
841 of_node_put(port);
842 return sun4i_tcon_find_engine_traverse(drv, node);
843}
844
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100845static int sun4i_tcon_bind(struct device *dev, struct device *master,
846 void *data)
847{
848 struct drm_device *drm = data;
849 struct sun4i_drv *drv = drm->dev_private;
Icenowy Zheng87969332017-05-17 22:47:17 +0800850 struct sunxi_engine *engine;
Maxime Riparda0c12142017-12-21 12:02:33 +0100851 struct device_node *remote;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100852 struct sun4i_tcon *tcon;
Maxime Riparda0c12142017-12-21 12:02:33 +0100853 bool has_lvds_rst, has_lvds_alt, can_lvds;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100854 int ret;
855
Icenowy Zheng87969332017-05-17 22:47:17 +0800856 engine = sun4i_tcon_find_engine(drv, dev->of_node);
857 if (IS_ERR(engine)) {
858 dev_err(dev, "Couldn't find matching engine\n");
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800859 return -EPROBE_DEFER;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800860 }
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800861
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100862 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
863 if (!tcon)
864 return -ENOMEM;
865 dev_set_drvdata(dev, tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100866 tcon->drm = drm;
Maxime Ripardae558112016-07-19 15:17:27 +0200867 tcon->dev = dev;
Icenowy Zheng87969332017-05-17 22:47:17 +0800868 tcon->id = engine->id;
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800869 tcon->quirks = of_device_get_match_data(dev);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100870
871 tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
872 if (IS_ERR(tcon->lcd_rst)) {
873 dev_err(dev, "Couldn't get our reset line\n");
874 return PTR_ERR(tcon->lcd_rst);
875 }
876
877 /* Make sure our TCON is reset */
Chen-Yu Tsaid57294c2017-09-08 17:00:16 +0800878 ret = reset_control_reset(tcon->lcd_rst);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100879 if (ret) {
880 dev_err(dev, "Couldn't deassert our reset line\n");
881 return ret;
882 }
883
Maxime Riparda0c12142017-12-21 12:02:33 +0100884 /*
885 * This can only be made optional since we've had DT nodes
886 * without the LVDS reset properties.
887 *
888 * If the property is missing, just disable LVDS, and print a
889 * warning.
890 */
891 tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds");
892 if (IS_ERR(tcon->lvds_rst)) {
893 dev_err(dev, "Couldn't get our reset line\n");
894 return PTR_ERR(tcon->lvds_rst);
895 } else if (tcon->lvds_rst) {
896 has_lvds_rst = true;
897 reset_control_reset(tcon->lvds_rst);
898 } else {
899 has_lvds_rst = false;
900 }
901
902 /*
903 * This can only be made optional since we've had DT nodes
904 * without the LVDS reset properties.
905 *
906 * If the property is missing, just disable LVDS, and print a
907 * warning.
908 */
909 if (tcon->quirks->has_lvds_alt) {
910 tcon->lvds_pll = devm_clk_get(dev, "lvds-alt");
911 if (IS_ERR(tcon->lvds_pll)) {
912 if (PTR_ERR(tcon->lvds_pll) == -ENOENT) {
913 has_lvds_alt = false;
914 } else {
915 dev_err(dev, "Couldn't get the LVDS PLL\n");
916 return PTR_ERR(tcon->lvds_rst);
917 }
918 } else {
919 has_lvds_alt = true;
920 }
921 }
922
923 if (!has_lvds_rst || (tcon->quirks->has_lvds_alt && !has_lvds_alt)) {
924 dev_warn(dev,
925 "Missing LVDS properties, Please upgrade your DT\n");
926 dev_warn(dev, "LVDS output disabled\n");
927 can_lvds = false;
928 } else {
929 can_lvds = true;
930 }
931
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100932 ret = sun4i_tcon_init_clocks(dev, tcon);
933 if (ret) {
934 dev_err(dev, "Couldn't init our TCON clocks\n");
935 goto err_assert_reset;
936 }
937
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800938 ret = sun4i_tcon_init_regmap(dev, tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100939 if (ret) {
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800940 dev_err(dev, "Couldn't init our TCON regmap\n");
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100941 goto err_free_clocks;
942 }
943
Jernej Skrabec34d698f2018-02-14 21:09:01 +0100944 if (tcon->quirks->has_channel_0) {
945 ret = sun4i_dclk_create(dev, tcon);
946 if (ret) {
947 dev_err(dev, "Couldn't create our TCON dot clock\n");
948 goto err_free_clocks;
949 }
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800950 }
951
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100952 ret = sun4i_tcon_init_irq(dev, tcon);
953 if (ret) {
954 dev_err(dev, "Couldn't init our TCON interrupts\n");
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800955 goto err_free_dotclock;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100956 }
957
Icenowy Zheng87969332017-05-17 22:47:17 +0800958 tcon->crtc = sun4i_crtc_init(drm, engine, tcon);
Chen-Yu Tsai46cce6d2017-02-23 16:05:37 +0800959 if (IS_ERR(tcon->crtc)) {
960 dev_err(dev, "Couldn't create our CRTC\n");
961 ret = PTR_ERR(tcon->crtc);
Maxime Ripard92411f62017-12-07 16:58:50 +0100962 goto err_free_dotclock;
Chen-Yu Tsai46cce6d2017-02-23 16:05:37 +0800963 }
964
Maxime Riparda0c12142017-12-21 12:02:33 +0100965 /*
966 * If we have an LVDS panel connected to the TCON, we should
967 * just probe the LVDS connector. Otherwise, just probe RGB as
968 * we used to.
969 */
970 remote = of_graph_get_remote_node(dev->of_node, 1, 0);
971 if (of_device_is_compatible(remote, "panel-lvds"))
972 if (can_lvds)
973 ret = sun4i_lvds_init(drm, tcon);
974 else
975 ret = -EINVAL;
976 else
977 ret = sun4i_rgb_init(drm, tcon);
978 of_node_put(remote);
979
Chen-Yu Tsai13fef092016-05-17 23:56:06 +0800980 if (ret < 0)
Maxime Ripard92411f62017-12-07 16:58:50 +0100981 goto err_free_dotclock;
Chen-Yu Tsai13fef092016-05-17 23:56:06 +0800982
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +0800983 if (tcon->quirks->needs_de_be_mux) {
984 /*
985 * We assume there is no dynamic muxing of backends
986 * and TCONs, so we select the backend with same ID.
987 *
988 * While dynamic selection might be interesting, since
989 * the CRTC is tied to the TCON, while the layers are
990 * tied to the backends, this means, we will need to
991 * switch between groups of layers. There might not be
992 * a way to represent this constraint in DRM.
993 */
994 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
995 SUN4I_TCON0_CTL_SRC_SEL_MASK,
996 tcon->id);
997 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
998 SUN4I_TCON1_CTL_SRC_SEL_MASK,
999 tcon->id);
1000 }
1001
Chen-Yu Tsai80a58242017-04-21 16:38:50 +08001002 list_add_tail(&tcon->list, &drv->tcon_list);
1003
Chen-Yu Tsai13fef092016-05-17 23:56:06 +08001004 return 0;
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001005
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +08001006err_free_dotclock:
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001007 if (tcon->quirks->has_channel_0)
1008 sun4i_dclk_free(tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001009err_free_clocks:
1010 sun4i_tcon_free_clocks(tcon);
1011err_assert_reset:
1012 reset_control_assert(tcon->lcd_rst);
1013 return ret;
1014}
1015
1016static void sun4i_tcon_unbind(struct device *dev, struct device *master,
1017 void *data)
1018{
1019 struct sun4i_tcon *tcon = dev_get_drvdata(dev);
1020
Chen-Yu Tsai80a58242017-04-21 16:38:50 +08001021 list_del(&tcon->list);
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001022 if (tcon->quirks->has_channel_0)
1023 sun4i_dclk_free(tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001024 sun4i_tcon_free_clocks(tcon);
1025}
1026
Julia Lawalldfeb6932016-11-12 18:19:58 +01001027static const struct component_ops sun4i_tcon_ops = {
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001028 .bind = sun4i_tcon_bind,
1029 .unbind = sun4i_tcon_unbind,
1030};
1031
1032static int sun4i_tcon_probe(struct platform_device *pdev)
1033{
Maxime Ripard29e57fa2015-10-29 09:37:32 +01001034 struct device_node *node = pdev->dev.of_node;
Maxime Ripard894f5a92016-04-11 12:16:33 +02001035 struct drm_bridge *bridge;
Maxime Ripard29e57fa2015-10-29 09:37:32 +01001036 struct drm_panel *panel;
Rob Herringebc94462017-03-29 13:55:46 -05001037 int ret;
Maxime Ripard29e57fa2015-10-29 09:37:32 +01001038
Rob Herringebc94462017-03-29 13:55:46 -05001039 ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
1040 if (ret == -EPROBE_DEFER)
1041 return ret;
Maxime Ripard29e57fa2015-10-29 09:37:32 +01001042
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001043 return component_add(&pdev->dev, &sun4i_tcon_ops);
1044}
1045
1046static int sun4i_tcon_remove(struct platform_device *pdev)
1047{
1048 component_del(&pdev->dev, &sun4i_tcon_ops);
1049
1050 return 0;
1051}
1052
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +08001053/* platform specific TCON muxing callbacks */
Jonathan Liu4bb206b2017-10-17 20:17:59 +08001054static int sun4i_a10_tcon_set_mux(struct sun4i_tcon *tcon,
1055 const struct drm_encoder *encoder)
1056{
1057 struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
1058 u32 shift;
1059
1060 if (!tcon0)
1061 return -EINVAL;
1062
1063 switch (encoder->encoder_type) {
1064 case DRM_MODE_ENCODER_TMDS:
1065 /* HDMI */
1066 shift = 8;
1067 break;
1068 default:
1069 return -EINVAL;
1070 }
1071
1072 regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
1073 0x3 << shift, tcon->id << shift);
1074
1075 return 0;
1076}
1077
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +08001078static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,
Maxime Ripardabcb8762017-10-17 11:06:10 +02001079 const struct drm_encoder *encoder)
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +08001080{
1081 u32 val;
1082
1083 if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
1084 val = 1;
1085 else
1086 val = 0;
1087
1088 /*
1089 * FIXME: Undocumented bits
1090 */
1091 return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
1092}
1093
Chen-Yu Tsai67e32642017-10-10 11:19:59 +08001094static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,
Maxime Ripardabcb8762017-10-17 11:06:10 +02001095 const struct drm_encoder *encoder)
Chen-Yu Tsai67e32642017-10-10 11:19:59 +08001096{
1097 struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
1098 u32 shift;
1099
1100 if (!tcon0)
1101 return -EINVAL;
1102
1103 switch (encoder->encoder_type) {
1104 case DRM_MODE_ENCODER_TMDS:
1105 /* HDMI */
1106 shift = 8;
1107 break;
1108 default:
1109 /* TODO A31 has MIPI DSI but A31s does not */
1110 return -EINVAL;
1111 }
1112
1113 regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
1114 0x3 << shift, tcon->id << shift);
1115
1116 return 0;
1117}
1118
Jonathan Liu4bb206b2017-10-17 20:17:59 +08001119static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001120 .has_channel_0 = true,
Jonathan Liu4bb206b2017-10-17 20:17:59 +08001121 .has_channel_1 = true,
1122 .set_mux = sun4i_a10_tcon_set_mux,
1123};
1124
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001125static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001126 .has_channel_0 = true,
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +08001127 .has_channel_1 = true,
1128 .set_mux = sun5i_a13_tcon_set_mux,
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001129};
1130
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +08001131static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001132 .has_channel_0 = true,
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +08001133 .has_channel_1 = true,
Maxime Riparda0c12142017-12-21 12:02:33 +01001134 .has_lvds_alt = true,
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +08001135 .needs_de_be_mux = true,
Chen-Yu Tsai67e32642017-10-10 11:19:59 +08001136 .set_mux = sun6i_tcon_set_mux,
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +08001137};
1138
1139static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001140 .has_channel_0 = true,
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +08001141 .has_channel_1 = true,
1142 .needs_de_be_mux = true,
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +08001143};
1144
Jonathan Liuaaddb6d2017-10-17 20:18:02 +08001145static const struct sun4i_tcon_quirks sun7i_a20_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001146 .has_channel_0 = true,
Jonathan Liuaaddb6d2017-10-17 20:18:02 +08001147 .has_channel_1 = true,
1148 /* Same display pipeline structure as A10 */
1149 .set_mux = sun4i_a10_tcon_set_mux,
1150};
1151
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001152static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001153 .has_channel_0 = true,
Maxime Riparda0c12142017-12-21 12:02:33 +01001154 .has_lvds_alt = true,
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001155};
1156
Maxime Ripard2f0d7bb2017-12-21 12:02:34 +01001157static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001158 .has_channel_0 = true,
Maxime Ripard2f0d7bb2017-12-21 12:02:34 +01001159};
1160
Jernej Skrabec05adc892018-02-14 21:09:02 +01001161static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = {
1162 .has_channel_1 = true,
1163};
1164
Icenowy Zheng1a0edb32017-05-17 22:47:22 +08001165static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
Jernej Skrabec34d698f2018-02-14 21:09:01 +01001166 .has_channel_0 = true,
Icenowy Zheng1a0edb32017-05-17 22:47:22 +08001167};
1168
Chen-Yu Tsaiff71c2c2017-11-27 16:46:32 +08001169/* sun4i_drv uses this list to check if a device node is a TCON */
1170const struct of_device_id sun4i_tcon_of_table[] = {
Jonathan Liu4bb206b2017-10-17 20:17:59 +08001171 { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks },
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001172 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +08001173 { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
1174 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
Jonathan Liuaaddb6d2017-10-17 20:18:02 +08001175 { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks },
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +08001176 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
Maxime Ripard2f0d7bb2017-12-21 12:02:34 +01001177 { .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks },
Jernej Skrabec05adc892018-02-14 21:09:02 +01001178 { .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks },
Icenowy Zheng1a0edb32017-05-17 22:47:22 +08001179 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001180 { }
1181};
1182MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
Chen-Yu Tsaiff71c2c2017-11-27 16:46:32 +08001183EXPORT_SYMBOL(sun4i_tcon_of_table);
Maxime Ripard9026e0d2015-10-29 09:36:23 +01001184
1185static struct platform_driver sun4i_tcon_platform_driver = {
1186 .probe = sun4i_tcon_probe,
1187 .remove = sun4i_tcon_remove,
1188 .driver = {
1189 .name = "sun4i-tcon",
1190 .of_match_table = sun4i_tcon_of_table,
1191 },
1192};
1193module_platform_driver(sun4i_tcon_platform_driver);
1194
1195MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
1196MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");
1197MODULE_LICENSE("GPL");