blob: 41d325c0420fa234c546034ac40c5484afbf9feb [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 Ripard29e57fa2015-10-29 09:37:32 +010034#include "sun4i_rgb.h"
Maxime Ripard9026e0d2015-10-29 09:36:23 +010035#include "sun4i_tcon.h"
Icenowy Zheng87969332017-05-17 22:47:17 +080036#include "sunxi_engine.h"
Maxime Ripard9026e0d2015-10-29 09:36:23 +010037
Maxime Ripard45e88f92017-10-17 11:06:12 +020038static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
39 bool enabled)
Maxime Ripard9026e0d2015-10-29 09:36:23 +010040{
Maxime Ripard45e88f92017-10-17 11:06:12 +020041 struct clk *clk;
Maxime Ripard9026e0d2015-10-29 09:36:23 +010042
Maxime Ripard45e88f92017-10-17 11:06:12 +020043 switch (channel) {
44 case 0:
Maxime Ripard9026e0d2015-10-29 09:36:23 +010045 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
46 SUN4I_TCON0_CTL_TCON_ENABLE,
Maxime Ripard45e88f92017-10-17 11:06:12 +020047 enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);
48 clk = tcon->dclk;
49 break;
50 case 1:
51 WARN_ON(!tcon->quirks->has_channel_1);
52 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
53 SUN4I_TCON1_CTL_TCON_ENABLE,
54 enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0);
55 clk = tcon->sclk1;
56 break;
57 default:
58 DRM_WARN("Unknown channel... doing nothing\n");
Maxime Ripard8e924042016-01-07 12:32:07 +010059 return;
Maxime Ripard9026e0d2015-10-29 09:36:23 +010060 }
Maxime Ripard8e924042016-01-07 12:32:07 +010061
Maxime Ripard45e88f92017-10-17 11:06:12 +020062 if (enabled)
63 clk_prepare_enable(clk);
64 else
65 clk_disable_unprepare(clk);
Maxime Ripard9026e0d2015-10-29 09:36:23 +010066}
Maxime Ripard45e88f92017-10-17 11:06:12 +020067
68void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
69 const struct drm_encoder *encoder,
70 bool enabled)
71{
72 int channel;
73
74 switch (encoder->encoder_type) {
75 case DRM_MODE_ENCODER_NONE:
76 channel = 0;
77 break;
78 case DRM_MODE_ENCODER_TMDS:
79 case DRM_MODE_ENCODER_TVDAC:
80 channel = 1;
81 break;
82 default:
83 DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
84 return;
85 }
86
87 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
88 SUN4I_TCON_GCTL_TCON_ENABLE,
89 enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
90
91 sun4i_tcon_channel_set_status(tcon, channel, enabled);
92}
Maxime Ripard9026e0d2015-10-29 09:36:23 +010093
94void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
95{
96 u32 mask, val = 0;
97
98 DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");
99
100 mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |
101 SUN4I_TCON_GINT0_VBLANK_ENABLE(1);
102
103 if (enable)
104 val = mask;
105
106 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val);
107}
108EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
109
Chen-Yu Tsai67e32642017-10-10 11:19:59 +0800110/*
111 * This function is a helper for TCON output muxing. The TCON output
112 * muxing control register in earlier SoCs (without the TCON TOP block)
113 * are located in TCON0. This helper returns a pointer to TCON0's
114 * sun4i_tcon structure, or NULL if not found.
115 */
116static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
117{
118 struct sun4i_drv *drv = drm->dev_private;
119 struct sun4i_tcon *tcon;
120
121 list_for_each_entry(tcon, &drv->tcon_list, list)
122 if (tcon->id == 0)
123 return tcon;
124
125 dev_warn(drm->dev,
126 "TCON0 not found, display output muxing may not work\n");
127
128 return NULL;
129}
130
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200131void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
Maxime Ripardabcb8762017-10-17 11:06:10 +0200132 const struct drm_encoder *encoder)
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200133{
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800134 int ret = -ENOTSUPP;
Maxime Ripardb7cb9b92017-05-27 18:09:28 +0200135
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800136 if (tcon->quirks->set_mux)
137 ret = tcon->quirks->set_mux(tcon, encoder);
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200138
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800139 DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",
140 encoder->name, encoder->crtc->name, ret);
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200141}
Maxime Ripardf8c73f42017-05-27 18:09:27 +0200142
Maxime Ripard961c6452017-10-17 11:06:11 +0200143static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode,
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100144 int channel)
145{
146 int delay = mode->vtotal - mode->vdisplay;
147
148 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
149 delay /= 2;
150
151 if (channel == 1)
152 delay -= 2;
153
154 delay = min(delay, 30);
155
156 DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay);
157
158 return delay;
159}
160
Maxime Ripardba19c532017-10-17 11:06:14 +0200161static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
162 const struct drm_display_mode *mode)
163{
164 /* Configure the dot clock */
165 clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
166
167 /* Set the resolution */
168 regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,
169 SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |
170 SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
171}
172
173static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
174 const struct drm_display_mode *mode)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100175{
176 unsigned int bp, hsync, vsync;
177 u8 clk_delay;
178 u32 val = 0;
179
Maxime Ripardec08d592017-12-21 12:02:32 +0100180 tcon->dclk_min_div = 6;
181 tcon->dclk_max_div = 127;
Maxime Ripardba19c532017-10-17 11:06:14 +0200182 sun4i_tcon0_mode_set_common(tcon, mode);
Chen-Yu Tsai86cf6782017-04-25 23:25:04 +0800183
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100184 /* Adjust clock delay */
185 clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
186 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
187 SUN4I_TCON0_CTL_CLK_DELAY_MASK,
188 SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));
189
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100190 /*
191 * This is called a backporch in the register documentation,
Chen-Yu Tsai23a1cb12017-03-09 18:05:25 +0800192 * but it really is the back porch + hsync
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100193 */
194 bp = mode->crtc_htotal - mode->crtc_hsync_start;
195 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
196 mode->crtc_htotal, bp);
197
198 /* Set horizontal display timings */
199 regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,
200 SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) |
201 SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));
202
203 /*
204 * This is called a backporch in the register documentation,
Chen-Yu Tsai23a1cb12017-03-09 18:05:25 +0800205 * but it really is the back porch + hsync
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100206 */
207 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
208 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
209 mode->crtc_vtotal, bp);
210
211 /* Set vertical display timings */
212 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
Maxime Riparda88cbbd2017-05-27 18:09:30 +0200213 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100214 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
215
216 /* Set Hsync and Vsync length */
217 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
218 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
219 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
220 regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG,
221 SUN4I_TCON0_BASIC3_V_SYNC(vsync) |
222 SUN4I_TCON0_BASIC3_H_SYNC(hsync));
223
224 /* Setup the polarity of the various signals */
225 if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))
226 val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;
227
228 if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
229 val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;
230
231 regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG,
232 SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE,
233 val);
234
235 /* Map output pins to channel 0 */
236 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
237 SUN4I_TCON_GCTL_IOMAP_MASK,
238 SUN4I_TCON_GCTL_IOMAP_TCON0);
239
240 /* Enable the output on the pins */
241 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0);
242}
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100243
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200244static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
245 const struct drm_display_mode *mode)
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100246{
Maxime Ripardb8317a32017-05-27 18:09:31 +0200247 unsigned int bp, hsync, vsync, vtotal;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100248 u8 clk_delay;
249 u32 val;
250
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800251 WARN_ON(!tcon->quirks->has_channel_1);
Maxime Ripard8e924042016-01-07 12:32:07 +0100252
Chen-Yu Tsai86cf6782017-04-25 23:25:04 +0800253 /* Configure the dot clock */
254 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
255
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100256 /* Adjust clock delay */
257 clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
258 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
259 SUN4I_TCON1_CTL_CLK_DELAY_MASK,
260 SUN4I_TCON1_CTL_CLK_DELAY(clk_delay));
261
262 /* Set interlaced mode */
263 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
264 val = SUN4I_TCON1_CTL_INTERLACE_ENABLE;
265 else
266 val = 0;
267 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
268 SUN4I_TCON1_CTL_INTERLACE_ENABLE,
269 val);
270
271 /* Set the input resolution */
272 regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,
273 SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |
274 SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));
275
276 /* Set the upscaling resolution */
277 regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,
278 SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |
279 SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));
280
281 /* Set the output resolution */
282 regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,
283 SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |
284 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
285
286 /* Set horizontal display timings */
Maxime Ripard3cb2f462017-05-27 18:09:29 +0200287 bp = mode->crtc_htotal - mode->crtc_hsync_start;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100288 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
289 mode->htotal, bp);
290 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
291 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
292 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
293
Maxime Ripard3cb2f462017-05-27 18:09:29 +0200294 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100295 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
Maxime Ripardb8317a32017-05-27 18:09:31 +0200296 mode->crtc_vtotal, bp);
297
298 /*
299 * The vertical resolution needs to be doubled in all
300 * cases. We could use crtc_vtotal and always multiply by two,
301 * but that leads to a rounding error in interlace when vtotal
302 * is odd.
303 *
304 * This happens with TV's PAL for example, where vtotal will
305 * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be
306 * 624, which apparently confuses the hardware.
307 *
308 * To work around this, we will always use vtotal, and
309 * multiply by two only if we're not in interlace.
310 */
311 vtotal = mode->vtotal;
312 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
313 vtotal = vtotal * 2;
314
315 /* Set vertical display timings */
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100316 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
Maxime Ripardb8317a32017-05-27 18:09:31 +0200317 SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) |
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100318 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
319
320 /* Set Hsync and Vsync length */
321 hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
322 vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
323 DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);
324 regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG,
325 SUN4I_TCON1_BASIC5_V_SYNC(vsync) |
326 SUN4I_TCON1_BASIC5_H_SYNC(hsync));
327
328 /* Map output pins to channel 1 */
329 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
330 SUN4I_TCON_GCTL_IOMAP_MASK,
331 SUN4I_TCON_GCTL_IOMAP_TCON1);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100332}
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200333
334void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
335 const struct drm_encoder *encoder,
336 const struct drm_display_mode *mode)
337{
338 switch (encoder->encoder_type) {
339 case DRM_MODE_ENCODER_NONE:
Maxime Ripardba19c532017-10-17 11:06:14 +0200340 sun4i_tcon0_mode_set_rgb(tcon, mode);
Maxime Ripard5b8f0912017-10-17 11:06:13 +0200341 sun4i_tcon_set_mux(tcon, 0, encoder);
342 break;
343 case DRM_MODE_ENCODER_TVDAC:
344 case DRM_MODE_ENCODER_TMDS:
345 sun4i_tcon1_mode_set(tcon, mode);
346 sun4i_tcon_set_mux(tcon, 1, encoder);
347 break;
348 default:
349 DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
350 }
351}
352EXPORT_SYMBOL(sun4i_tcon_mode_set);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100353
354static void sun4i_tcon_finish_page_flip(struct drm_device *dev,
355 struct sun4i_crtc *scrtc)
356{
357 unsigned long flags;
358
359 spin_lock_irqsave(&dev->event_lock, flags);
360 if (scrtc->event) {
361 drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event);
362 drm_crtc_vblank_put(&scrtc->crtc);
363 scrtc->event = NULL;
364 }
365 spin_unlock_irqrestore(&dev->event_lock, flags);
366}
367
368static irqreturn_t sun4i_tcon_handler(int irq, void *private)
369{
370 struct sun4i_tcon *tcon = private;
371 struct drm_device *drm = tcon->drm;
Chen-Yu Tsai46cce6d2017-02-23 16:05:37 +0800372 struct sun4i_crtc *scrtc = tcon->crtc;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100373 unsigned int status;
374
375 regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
376
377 if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
378 SUN4I_TCON_GINT0_VBLANK_INT(1))))
379 return IRQ_NONE;
380
381 drm_crtc_handle_vblank(&scrtc->crtc);
382 sun4i_tcon_finish_page_flip(drm, scrtc);
383
384 /* Acknowledge the interrupt */
385 regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,
386 SUN4I_TCON_GINT0_VBLANK_INT(0) |
387 SUN4I_TCON_GINT0_VBLANK_INT(1),
388 0);
389
390 return IRQ_HANDLED;
391}
392
393static int sun4i_tcon_init_clocks(struct device *dev,
394 struct sun4i_tcon *tcon)
395{
396 tcon->clk = devm_clk_get(dev, "ahb");
397 if (IS_ERR(tcon->clk)) {
398 dev_err(dev, "Couldn't get the TCON bus clock\n");
399 return PTR_ERR(tcon->clk);
400 }
401 clk_prepare_enable(tcon->clk);
402
403 tcon->sclk0 = devm_clk_get(dev, "tcon-ch0");
404 if (IS_ERR(tcon->sclk0)) {
405 dev_err(dev, "Couldn't get the TCON channel 0 clock\n");
406 return PTR_ERR(tcon->sclk0);
407 }
408
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800409 if (tcon->quirks->has_channel_1) {
Maxime Ripard8e924042016-01-07 12:32:07 +0100410 tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
411 if (IS_ERR(tcon->sclk1)) {
412 dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
413 return PTR_ERR(tcon->sclk1);
414 }
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100415 }
416
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800417 return 0;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100418}
419
420static void sun4i_tcon_free_clocks(struct sun4i_tcon *tcon)
421{
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100422 clk_disable_unprepare(tcon->clk);
423}
424
425static int sun4i_tcon_init_irq(struct device *dev,
426 struct sun4i_tcon *tcon)
427{
428 struct platform_device *pdev = to_platform_device(dev);
429 int irq, ret;
430
431 irq = platform_get_irq(pdev, 0);
432 if (irq < 0) {
433 dev_err(dev, "Couldn't retrieve the TCON interrupt\n");
434 return irq;
435 }
436
437 ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0,
438 dev_name(dev), tcon);
439 if (ret) {
440 dev_err(dev, "Couldn't request the IRQ\n");
441 return ret;
442 }
443
444 return 0;
445}
446
447static struct regmap_config sun4i_tcon_regmap_config = {
448 .reg_bits = 32,
449 .val_bits = 32,
450 .reg_stride = 4,
451 .max_register = 0x800,
452};
453
454static int sun4i_tcon_init_regmap(struct device *dev,
455 struct sun4i_tcon *tcon)
456{
457 struct platform_device *pdev = to_platform_device(dev);
458 struct resource *res;
459 void __iomem *regs;
460
461 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
462 regs = devm_ioremap_resource(dev, res);
Wei Yongjunaf346f52016-08-26 14:25:25 +0000463 if (IS_ERR(regs))
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100464 return PTR_ERR(regs);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100465
466 tcon->regs = devm_regmap_init_mmio(dev, regs,
467 &sun4i_tcon_regmap_config);
468 if (IS_ERR(tcon->regs)) {
469 dev_err(dev, "Couldn't create the TCON regmap\n");
470 return PTR_ERR(tcon->regs);
471 }
472
473 /* Make sure the TCON is disabled and all IRQs are off */
474 regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);
475 regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
476 regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0);
477
478 /* Disable IO lines and set them to tristate */
479 regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);
480 regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);
481
482 return 0;
483}
484
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800485/*
486 * On SoCs with the old display pipeline design (Display Engine 1.0),
487 * the TCON is always tied to just one backend. Hence we can traverse
488 * the of_graph upwards to find the backend our tcon is connected to,
489 * and take its ID as our own.
490 *
491 * We can either identify backends from their compatible strings, which
492 * means maintaining a large list of them. Or, since the backend is
493 * registered and binded before the TCON, we can just go through the
494 * list of registered backends and compare the device node.
Icenowy Zheng87969332017-05-17 22:47:17 +0800495 *
496 * As the structures now store engines instead of backends, here this
497 * function in fact searches the corresponding engine, and the ID is
498 * requested via the get_id function of the engine.
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800499 */
Chen-Yu Tsaie8d5bbf2017-09-08 15:50:12 +0800500static struct sunxi_engine *
501sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,
502 struct device_node *node)
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800503{
504 struct device_node *port, *ep, *remote;
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800505 struct sunxi_engine *engine = ERR_PTR(-EINVAL);
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800506
507 port = of_graph_get_port_by_id(node, 0);
508 if (!port)
509 return ERR_PTR(-EINVAL);
510
Chen-Yu Tsai14696192017-09-08 15:50:11 +0800511 /*
512 * This only works if there is only one path from the TCON
513 * to any display engine. Otherwise the probe order of the
514 * TCONs and display engines is not guaranteed. They may
515 * either bind to the wrong one, or worse, bind to the same
516 * one if additional checks are not done.
517 *
518 * Bail out if there are multiple input connections.
519 */
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800520 if (of_get_available_child_count(port) != 1)
521 goto out_put_port;
Chen-Yu Tsai14696192017-09-08 15:50:11 +0800522
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800523 /* Get the first connection without specifying an ID */
524 ep = of_get_next_available_child(port, NULL);
525 if (!ep)
526 goto out_put_port;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800527
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800528 remote = of_graph_get_remote_port_parent(ep);
529 if (!remote)
530 goto out_put_ep;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800531
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800532 /* does this node match any registered engines? */
533 list_for_each_entry(engine, &drv->engine_list, list)
534 if (remote == engine->node)
535 goto out_put_remote;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800536
Chen-Yu Tsaibe3fe0f2017-09-08 15:50:13 +0800537 /* keep looking through upstream ports */
538 engine = sun4i_tcon_find_engine_traverse(drv, remote);
539
540out_put_remote:
541 of_node_put(remote);
542out_put_ep:
543 of_node_put(ep);
544out_put_port:
545 of_node_put(port);
546
547 return engine;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800548}
549
Chen-Yu Tsaie8d5bbf2017-09-08 15:50:12 +0800550/*
551 * The device tree binding says that the remote endpoint ID of any
552 * connection between components, up to and including the TCON, of
553 * the display pipeline should be equal to the actual ID of the local
554 * component. Thus we can look at any one of the input connections of
555 * the TCONs, and use that connection's remote endpoint ID as our own.
556 *
557 * Since the user of this function already finds the input port,
558 * the port is passed in directly without further checks.
559 */
560static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
561{
562 struct device_node *ep;
563 int ret = -EINVAL;
564
565 /* try finding an upstream endpoint */
566 for_each_available_child_of_node(port, ep) {
567 struct device_node *remote;
568 u32 reg;
569
570 remote = of_graph_get_remote_endpoint(ep);
571 if (!remote)
572 continue;
573
574 ret = of_property_read_u32(remote, "reg", &reg);
575 if (ret)
576 continue;
577
578 ret = reg;
579 }
580
581 return ret;
582}
583
584/*
585 * Once we know the TCON's id, we can look through the list of
586 * engines to find a matching one. We assume all engines have
587 * been probed and added to the list.
588 */
589static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,
590 int id)
591{
592 struct sunxi_engine *engine;
593
594 list_for_each_entry(engine, &drv->engine_list, list)
595 if (engine->id == id)
596 return engine;
597
598 return ERR_PTR(-EINVAL);
599}
600
601/*
602 * On SoCs with the old display pipeline design (Display Engine 1.0),
603 * we assumed the TCON was always tied to just one backend. However
604 * this proved not to be the case. On the A31, the TCON can select
605 * either backend as its source. On the A20 (and likely on the A10),
606 * the backend can choose which TCON to output to.
607 *
608 * The device tree binding says that the remote endpoint ID of any
609 * connection between components, up to and including the TCON, of
610 * the display pipeline should be equal to the actual ID of the local
611 * component. Thus we should be able to look at any one of the input
612 * connections of the TCONs, and use that connection's remote endpoint
613 * ID as our own.
614 *
615 * However the connections between the backend and TCON were assumed
616 * to be always singular, and their endpoit IDs were all incorrectly
617 * set to 0. This means for these old device trees, we cannot just look
618 * up the remote endpoint ID of a TCON input endpoint. TCON1 would be
619 * incorrectly identified as TCON0.
620 *
621 * This function first checks if the TCON node has 2 input endpoints.
622 * If so, then the device tree is a corrected version, and it will use
623 * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above
624 * to fetch the ID and engine directly. If not, then it is likely an
625 * old device trees, where the endpoint IDs were incorrect, but did not
626 * have endpoint connections between the backend and TCON across
627 * different display pipelines. It will fall back to the old method of
628 * traversing the of_graph to try and find a matching engine by device
629 * node.
630 *
631 * In the case of single display pipeline device trees, either method
632 * works.
633 */
634static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
635 struct device_node *node)
636{
637 struct device_node *port;
638 struct sunxi_engine *engine;
639
640 port = of_graph_get_port_by_id(node, 0);
641 if (!port)
642 return ERR_PTR(-EINVAL);
643
644 /*
645 * Is this a corrected device tree with cross pipeline
646 * connections between the backend and TCON?
647 */
648 if (of_get_child_count(port) > 1) {
649 /* Get our ID directly from an upstream endpoint */
650 int id = sun4i_tcon_of_get_id_from_port(port);
651
652 /* Get our engine by matching our ID */
653 engine = sun4i_tcon_get_engine_by_id(drv, id);
654
655 of_node_put(port);
656 return engine;
657 }
658
659 /* Fallback to old method by traversing input endpoints */
660 of_node_put(port);
661 return sun4i_tcon_find_engine_traverse(drv, node);
662}
663
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100664static int sun4i_tcon_bind(struct device *dev, struct device *master,
665 void *data)
666{
667 struct drm_device *drm = data;
668 struct sun4i_drv *drv = drm->dev_private;
Icenowy Zheng87969332017-05-17 22:47:17 +0800669 struct sunxi_engine *engine;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100670 struct sun4i_tcon *tcon;
671 int ret;
672
Icenowy Zheng87969332017-05-17 22:47:17 +0800673 engine = sun4i_tcon_find_engine(drv, dev->of_node);
674 if (IS_ERR(engine)) {
675 dev_err(dev, "Couldn't find matching engine\n");
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800676 return -EPROBE_DEFER;
Chen-Yu Tsaib317fa32017-04-21 16:38:54 +0800677 }
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800678
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100679 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
680 if (!tcon)
681 return -ENOMEM;
682 dev_set_drvdata(dev, tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100683 tcon->drm = drm;
Maxime Ripardae558112016-07-19 15:17:27 +0200684 tcon->dev = dev;
Icenowy Zheng87969332017-05-17 22:47:17 +0800685 tcon->id = engine->id;
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800686 tcon->quirks = of_device_get_match_data(dev);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100687
688 tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
689 if (IS_ERR(tcon->lcd_rst)) {
690 dev_err(dev, "Couldn't get our reset line\n");
691 return PTR_ERR(tcon->lcd_rst);
692 }
693
694 /* Make sure our TCON is reset */
Chen-Yu Tsaid57294c2017-09-08 17:00:16 +0800695 ret = reset_control_reset(tcon->lcd_rst);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100696 if (ret) {
697 dev_err(dev, "Couldn't deassert our reset line\n");
698 return ret;
699 }
700
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100701 ret = sun4i_tcon_init_clocks(dev, tcon);
702 if (ret) {
703 dev_err(dev, "Couldn't init our TCON clocks\n");
704 goto err_assert_reset;
705 }
706
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800707 ret = sun4i_tcon_init_regmap(dev, tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100708 if (ret) {
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800709 dev_err(dev, "Couldn't init our TCON regmap\n");
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100710 goto err_free_clocks;
711 }
712
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800713 ret = sun4i_dclk_create(dev, tcon);
714 if (ret) {
715 dev_err(dev, "Couldn't create our TCON dot clock\n");
716 goto err_free_clocks;
717 }
718
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100719 ret = sun4i_tcon_init_irq(dev, tcon);
720 if (ret) {
721 dev_err(dev, "Couldn't init our TCON interrupts\n");
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800722 goto err_free_dotclock;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100723 }
724
Icenowy Zheng87969332017-05-17 22:47:17 +0800725 tcon->crtc = sun4i_crtc_init(drm, engine, tcon);
Chen-Yu Tsai46cce6d2017-02-23 16:05:37 +0800726 if (IS_ERR(tcon->crtc)) {
727 dev_err(dev, "Couldn't create our CRTC\n");
728 ret = PTR_ERR(tcon->crtc);
729 goto err_free_clocks;
730 }
731
Chen-Yu Tsaib9c85062017-02-23 16:05:41 +0800732 ret = sun4i_rgb_init(drm, tcon);
Chen-Yu Tsai13fef092016-05-17 23:56:06 +0800733 if (ret < 0)
734 goto err_free_clocks;
735
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +0800736 if (tcon->quirks->needs_de_be_mux) {
737 /*
738 * We assume there is no dynamic muxing of backends
739 * and TCONs, so we select the backend with same ID.
740 *
741 * While dynamic selection might be interesting, since
742 * the CRTC is tied to the TCON, while the layers are
743 * tied to the backends, this means, we will need to
744 * switch between groups of layers. There might not be
745 * a way to represent this constraint in DRM.
746 */
747 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
748 SUN4I_TCON0_CTL_SRC_SEL_MASK,
749 tcon->id);
750 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
751 SUN4I_TCON1_CTL_SRC_SEL_MASK,
752 tcon->id);
753 }
754
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800755 list_add_tail(&tcon->list, &drv->tcon_list);
756
Chen-Yu Tsai13fef092016-05-17 23:56:06 +0800757 return 0;
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100758
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800759err_free_dotclock:
760 sun4i_dclk_free(tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100761err_free_clocks:
762 sun4i_tcon_free_clocks(tcon);
763err_assert_reset:
764 reset_control_assert(tcon->lcd_rst);
765 return ret;
766}
767
768static void sun4i_tcon_unbind(struct device *dev, struct device *master,
769 void *data)
770{
771 struct sun4i_tcon *tcon = dev_get_drvdata(dev);
772
Chen-Yu Tsai80a58242017-04-21 16:38:50 +0800773 list_del(&tcon->list);
Chen-Yu Tsai4c7f16d2017-03-09 18:05:24 +0800774 sun4i_dclk_free(tcon);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100775 sun4i_tcon_free_clocks(tcon);
776}
777
Julia Lawalldfeb6932016-11-12 18:19:58 +0100778static const struct component_ops sun4i_tcon_ops = {
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100779 .bind = sun4i_tcon_bind,
780 .unbind = sun4i_tcon_unbind,
781};
782
783static int sun4i_tcon_probe(struct platform_device *pdev)
784{
Maxime Ripard29e57fa2015-10-29 09:37:32 +0100785 struct device_node *node = pdev->dev.of_node;
Maxime Ripard894f5a92016-04-11 12:16:33 +0200786 struct drm_bridge *bridge;
Maxime Ripard29e57fa2015-10-29 09:37:32 +0100787 struct drm_panel *panel;
Rob Herringebc94462017-03-29 13:55:46 -0500788 int ret;
Maxime Ripard29e57fa2015-10-29 09:37:32 +0100789
Rob Herringebc94462017-03-29 13:55:46 -0500790 ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
791 if (ret == -EPROBE_DEFER)
792 return ret;
Maxime Ripard29e57fa2015-10-29 09:37:32 +0100793
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100794 return component_add(&pdev->dev, &sun4i_tcon_ops);
795}
796
797static int sun4i_tcon_remove(struct platform_device *pdev)
798{
799 component_del(&pdev->dev, &sun4i_tcon_ops);
800
801 return 0;
802}
803
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800804/* platform specific TCON muxing callbacks */
Jonathan Liu4bb206b2017-10-17 20:17:59 +0800805static int sun4i_a10_tcon_set_mux(struct sun4i_tcon *tcon,
806 const struct drm_encoder *encoder)
807{
808 struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
809 u32 shift;
810
811 if (!tcon0)
812 return -EINVAL;
813
814 switch (encoder->encoder_type) {
815 case DRM_MODE_ENCODER_TMDS:
816 /* HDMI */
817 shift = 8;
818 break;
819 default:
820 return -EINVAL;
821 }
822
823 regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
824 0x3 << shift, tcon->id << shift);
825
826 return 0;
827}
828
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800829static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,
Maxime Ripardabcb8762017-10-17 11:06:10 +0200830 const struct drm_encoder *encoder)
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800831{
832 u32 val;
833
834 if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
835 val = 1;
836 else
837 val = 0;
838
839 /*
840 * FIXME: Undocumented bits
841 */
842 return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
843}
844
Chen-Yu Tsai67e32642017-10-10 11:19:59 +0800845static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,
Maxime Ripardabcb8762017-10-17 11:06:10 +0200846 const struct drm_encoder *encoder)
Chen-Yu Tsai67e32642017-10-10 11:19:59 +0800847{
848 struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
849 u32 shift;
850
851 if (!tcon0)
852 return -EINVAL;
853
854 switch (encoder->encoder_type) {
855 case DRM_MODE_ENCODER_TMDS:
856 /* HDMI */
857 shift = 8;
858 break;
859 default:
860 /* TODO A31 has MIPI DSI but A31s does not */
861 return -EINVAL;
862 }
863
864 regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
865 0x3 << shift, tcon->id << shift);
866
867 return 0;
868}
869
Jonathan Liu4bb206b2017-10-17 20:17:59 +0800870static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
871 .has_channel_1 = true,
872 .set_mux = sun4i_a10_tcon_set_mux,
873};
874
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800875static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
Chen-Yu Tsaiad537fb2017-10-10 11:19:58 +0800876 .has_channel_1 = true,
877 .set_mux = sun5i_a13_tcon_set_mux,
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800878};
879
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +0800880static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +0800881 .has_channel_1 = true,
882 .needs_de_be_mux = true,
Chen-Yu Tsai67e32642017-10-10 11:19:59 +0800883 .set_mux = sun6i_tcon_set_mux,
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +0800884};
885
886static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
Chen-Yu Tsai27e18de2017-09-08 15:50:14 +0800887 .has_channel_1 = true,
888 .needs_de_be_mux = true,
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +0800889};
890
Jonathan Liuaaddb6d2017-10-17 20:18:02 +0800891static const struct sun4i_tcon_quirks sun7i_a20_quirks = {
892 .has_channel_1 = true,
893 /* Same display pipeline structure as A10 */
894 .set_mux = sun4i_a10_tcon_set_mux,
895};
896
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800897static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
898 /* nothing is supported */
899};
900
Icenowy Zheng1a0edb32017-05-17 22:47:22 +0800901static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
902 /* nothing is supported */
903};
904
Chen-Yu Tsaiff71c2c2017-11-27 16:46:32 +0800905/* sun4i_drv uses this list to check if a device node is a TCON */
906const struct of_device_id sun4i_tcon_of_table[] = {
Jonathan Liu4bb206b2017-10-17 20:17:59 +0800907 { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks },
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800908 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
Chen-Yu Tsai93a5ec12016-10-20 11:43:40 +0800909 { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
910 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
Jonathan Liuaaddb6d2017-10-17 20:18:02 +0800911 { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks },
Chen-Yu Tsai91ea2f22016-10-20 11:43:39 +0800912 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
Icenowy Zheng1a0edb32017-05-17 22:47:22 +0800913 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100914 { }
915};
916MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
Chen-Yu Tsaiff71c2c2017-11-27 16:46:32 +0800917EXPORT_SYMBOL(sun4i_tcon_of_table);
Maxime Ripard9026e0d2015-10-29 09:36:23 +0100918
919static struct platform_driver sun4i_tcon_platform_driver = {
920 .probe = sun4i_tcon_probe,
921 .remove = sun4i_tcon_remove,
922 .driver = {
923 .name = "sun4i-tcon",
924 .of_match_table = sun4i_tcon_of_table,
925 },
926};
927module_platform_driver(sun4i_tcon_platform_driver);
928
929MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
930MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");
931MODULE_LICENSE("GPL");