xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision dee8268f8fb218c9e9b604a40f7dbdd395e910f9)
1*dee8268fSThierry Reding /*
2*dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
3*dee8268fSThierry Reding  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4*dee8268fSThierry Reding  *
5*dee8268fSThierry Reding  * This program is free software; you can redistribute it and/or modify
6*dee8268fSThierry Reding  * it under the terms of the GNU General Public License version 2 as
7*dee8268fSThierry Reding  * published by the Free Software Foundation.
8*dee8268fSThierry Reding  */
9*dee8268fSThierry Reding 
10*dee8268fSThierry Reding #include <linux/clk.h>
11*dee8268fSThierry Reding #include <linux/clk/tegra.h>
12*dee8268fSThierry Reding #include <linux/debugfs.h>
13*dee8268fSThierry Reding 
14*dee8268fSThierry Reding #include "dc.h"
15*dee8268fSThierry Reding #include "drm.h"
16*dee8268fSThierry Reding #include "gem.h"
17*dee8268fSThierry Reding 
18*dee8268fSThierry Reding struct tegra_plane {
19*dee8268fSThierry Reding 	struct drm_plane base;
20*dee8268fSThierry Reding 	unsigned int index;
21*dee8268fSThierry Reding };
22*dee8268fSThierry Reding 
23*dee8268fSThierry Reding static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
24*dee8268fSThierry Reding {
25*dee8268fSThierry Reding 	return container_of(plane, struct tegra_plane, base);
26*dee8268fSThierry Reding }
27*dee8268fSThierry Reding 
28*dee8268fSThierry Reding static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
29*dee8268fSThierry Reding 			      struct drm_framebuffer *fb, int crtc_x,
30*dee8268fSThierry Reding 			      int crtc_y, unsigned int crtc_w,
31*dee8268fSThierry Reding 			      unsigned int crtc_h, uint32_t src_x,
32*dee8268fSThierry Reding 			      uint32_t src_y, uint32_t src_w, uint32_t src_h)
33*dee8268fSThierry Reding {
34*dee8268fSThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
35*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
36*dee8268fSThierry Reding 	struct tegra_dc_window window;
37*dee8268fSThierry Reding 	unsigned int i;
38*dee8268fSThierry Reding 
39*dee8268fSThierry Reding 	memset(&window, 0, sizeof(window));
40*dee8268fSThierry Reding 	window.src.x = src_x >> 16;
41*dee8268fSThierry Reding 	window.src.y = src_y >> 16;
42*dee8268fSThierry Reding 	window.src.w = src_w >> 16;
43*dee8268fSThierry Reding 	window.src.h = src_h >> 16;
44*dee8268fSThierry Reding 	window.dst.x = crtc_x;
45*dee8268fSThierry Reding 	window.dst.y = crtc_y;
46*dee8268fSThierry Reding 	window.dst.w = crtc_w;
47*dee8268fSThierry Reding 	window.dst.h = crtc_h;
48*dee8268fSThierry Reding 	window.format = tegra_dc_format(fb->pixel_format);
49*dee8268fSThierry Reding 	window.bits_per_pixel = fb->bits_per_pixel;
50*dee8268fSThierry Reding 
51*dee8268fSThierry Reding 	for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
52*dee8268fSThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
53*dee8268fSThierry Reding 
54*dee8268fSThierry Reding 		window.base[i] = bo->paddr + fb->offsets[i];
55*dee8268fSThierry Reding 
56*dee8268fSThierry Reding 		/*
57*dee8268fSThierry Reding 		 * Tegra doesn't support different strides for U and V planes
58*dee8268fSThierry Reding 		 * so we display a warning if the user tries to display a
59*dee8268fSThierry Reding 		 * framebuffer with such a configuration.
60*dee8268fSThierry Reding 		 */
61*dee8268fSThierry Reding 		if (i >= 2) {
62*dee8268fSThierry Reding 			if (fb->pitches[i] != window.stride[1])
63*dee8268fSThierry Reding 				DRM_ERROR("unsupported UV-plane configuration\n");
64*dee8268fSThierry Reding 		} else {
65*dee8268fSThierry Reding 			window.stride[i] = fb->pitches[i];
66*dee8268fSThierry Reding 		}
67*dee8268fSThierry Reding 	}
68*dee8268fSThierry Reding 
69*dee8268fSThierry Reding 	return tegra_dc_setup_window(dc, p->index, &window);
70*dee8268fSThierry Reding }
71*dee8268fSThierry Reding 
72*dee8268fSThierry Reding static int tegra_plane_disable(struct drm_plane *plane)
73*dee8268fSThierry Reding {
74*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
75*dee8268fSThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
76*dee8268fSThierry Reding 	unsigned long value;
77*dee8268fSThierry Reding 
78*dee8268fSThierry Reding 	if (!plane->crtc)
79*dee8268fSThierry Reding 		return 0;
80*dee8268fSThierry Reding 
81*dee8268fSThierry Reding 	value = WINDOW_A_SELECT << p->index;
82*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
83*dee8268fSThierry Reding 
84*dee8268fSThierry Reding 	value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
85*dee8268fSThierry Reding 	value &= ~WIN_ENABLE;
86*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
87*dee8268fSThierry Reding 
88*dee8268fSThierry Reding 	tegra_dc_writel(dc, WIN_A_UPDATE << p->index, DC_CMD_STATE_CONTROL);
89*dee8268fSThierry Reding 	tegra_dc_writel(dc, WIN_A_ACT_REQ << p->index, DC_CMD_STATE_CONTROL);
90*dee8268fSThierry Reding 
91*dee8268fSThierry Reding 	return 0;
92*dee8268fSThierry Reding }
93*dee8268fSThierry Reding 
94*dee8268fSThierry Reding static void tegra_plane_destroy(struct drm_plane *plane)
95*dee8268fSThierry Reding {
96*dee8268fSThierry Reding 	tegra_plane_disable(plane);
97*dee8268fSThierry Reding 	drm_plane_cleanup(plane);
98*dee8268fSThierry Reding }
99*dee8268fSThierry Reding 
100*dee8268fSThierry Reding static const struct drm_plane_funcs tegra_plane_funcs = {
101*dee8268fSThierry Reding 	.update_plane = tegra_plane_update,
102*dee8268fSThierry Reding 	.disable_plane = tegra_plane_disable,
103*dee8268fSThierry Reding 	.destroy = tegra_plane_destroy,
104*dee8268fSThierry Reding };
105*dee8268fSThierry Reding 
106*dee8268fSThierry Reding static const uint32_t plane_formats[] = {
107*dee8268fSThierry Reding 	DRM_FORMAT_XBGR8888,
108*dee8268fSThierry Reding 	DRM_FORMAT_XRGB8888,
109*dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
110*dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
111*dee8268fSThierry Reding 	DRM_FORMAT_YUV420,
112*dee8268fSThierry Reding 	DRM_FORMAT_YUV422,
113*dee8268fSThierry Reding };
114*dee8268fSThierry Reding 
115*dee8268fSThierry Reding static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
116*dee8268fSThierry Reding {
117*dee8268fSThierry Reding 	unsigned int i;
118*dee8268fSThierry Reding 	int err = 0;
119*dee8268fSThierry Reding 
120*dee8268fSThierry Reding 	for (i = 0; i < 2; i++) {
121*dee8268fSThierry Reding 		struct tegra_plane *plane;
122*dee8268fSThierry Reding 
123*dee8268fSThierry Reding 		plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
124*dee8268fSThierry Reding 		if (!plane)
125*dee8268fSThierry Reding 			return -ENOMEM;
126*dee8268fSThierry Reding 
127*dee8268fSThierry Reding 		plane->index = 1 + i;
128*dee8268fSThierry Reding 
129*dee8268fSThierry Reding 		err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
130*dee8268fSThierry Reding 				     &tegra_plane_funcs, plane_formats,
131*dee8268fSThierry Reding 				     ARRAY_SIZE(plane_formats), false);
132*dee8268fSThierry Reding 		if (err < 0)
133*dee8268fSThierry Reding 			return err;
134*dee8268fSThierry Reding 	}
135*dee8268fSThierry Reding 
136*dee8268fSThierry Reding 	return 0;
137*dee8268fSThierry Reding }
138*dee8268fSThierry Reding 
139*dee8268fSThierry Reding static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
140*dee8268fSThierry Reding 			     struct drm_framebuffer *fb)
141*dee8268fSThierry Reding {
142*dee8268fSThierry Reding 	unsigned int format = tegra_dc_format(fb->pixel_format);
143*dee8268fSThierry Reding 	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
144*dee8268fSThierry Reding 	unsigned long value;
145*dee8268fSThierry Reding 
146*dee8268fSThierry Reding 	tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
147*dee8268fSThierry Reding 
148*dee8268fSThierry Reding 	value = fb->offsets[0] + y * fb->pitches[0] +
149*dee8268fSThierry Reding 		x * fb->bits_per_pixel / 8;
150*dee8268fSThierry Reding 
151*dee8268fSThierry Reding 	tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
152*dee8268fSThierry Reding 	tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
153*dee8268fSThierry Reding 	tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
154*dee8268fSThierry Reding 
155*dee8268fSThierry Reding 	value = GENERAL_UPDATE | WIN_A_UPDATE;
156*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
157*dee8268fSThierry Reding 
158*dee8268fSThierry Reding 	value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
159*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
160*dee8268fSThierry Reding 
161*dee8268fSThierry Reding 	return 0;
162*dee8268fSThierry Reding }
163*dee8268fSThierry Reding 
164*dee8268fSThierry Reding void tegra_dc_enable_vblank(struct tegra_dc *dc)
165*dee8268fSThierry Reding {
166*dee8268fSThierry Reding 	unsigned long value, flags;
167*dee8268fSThierry Reding 
168*dee8268fSThierry Reding 	spin_lock_irqsave(&dc->lock, flags);
169*dee8268fSThierry Reding 
170*dee8268fSThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
171*dee8268fSThierry Reding 	value |= VBLANK_INT;
172*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
173*dee8268fSThierry Reding 
174*dee8268fSThierry Reding 	spin_unlock_irqrestore(&dc->lock, flags);
175*dee8268fSThierry Reding }
176*dee8268fSThierry Reding 
177*dee8268fSThierry Reding void tegra_dc_disable_vblank(struct tegra_dc *dc)
178*dee8268fSThierry Reding {
179*dee8268fSThierry Reding 	unsigned long value, flags;
180*dee8268fSThierry Reding 
181*dee8268fSThierry Reding 	spin_lock_irqsave(&dc->lock, flags);
182*dee8268fSThierry Reding 
183*dee8268fSThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
184*dee8268fSThierry Reding 	value &= ~VBLANK_INT;
185*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
186*dee8268fSThierry Reding 
187*dee8268fSThierry Reding 	spin_unlock_irqrestore(&dc->lock, flags);
188*dee8268fSThierry Reding }
189*dee8268fSThierry Reding 
190*dee8268fSThierry Reding static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
191*dee8268fSThierry Reding {
192*dee8268fSThierry Reding 	struct drm_device *drm = dc->base.dev;
193*dee8268fSThierry Reding 	struct drm_crtc *crtc = &dc->base;
194*dee8268fSThierry Reding 	unsigned long flags, base;
195*dee8268fSThierry Reding 	struct tegra_bo *bo;
196*dee8268fSThierry Reding 
197*dee8268fSThierry Reding 	if (!dc->event)
198*dee8268fSThierry Reding 		return;
199*dee8268fSThierry Reding 
200*dee8268fSThierry Reding 	bo = tegra_fb_get_plane(crtc->fb, 0);
201*dee8268fSThierry Reding 
202*dee8268fSThierry Reding 	/* check if new start address has been latched */
203*dee8268fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
204*dee8268fSThierry Reding 	base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
205*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
206*dee8268fSThierry Reding 
207*dee8268fSThierry Reding 	if (base == bo->paddr + crtc->fb->offsets[0]) {
208*dee8268fSThierry Reding 		spin_lock_irqsave(&drm->event_lock, flags);
209*dee8268fSThierry Reding 		drm_send_vblank_event(drm, dc->pipe, dc->event);
210*dee8268fSThierry Reding 		drm_vblank_put(drm, dc->pipe);
211*dee8268fSThierry Reding 		dc->event = NULL;
212*dee8268fSThierry Reding 		spin_unlock_irqrestore(&drm->event_lock, flags);
213*dee8268fSThierry Reding 	}
214*dee8268fSThierry Reding }
215*dee8268fSThierry Reding 
216*dee8268fSThierry Reding void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
217*dee8268fSThierry Reding {
218*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
219*dee8268fSThierry Reding 	struct drm_device *drm = crtc->dev;
220*dee8268fSThierry Reding 	unsigned long flags;
221*dee8268fSThierry Reding 
222*dee8268fSThierry Reding 	spin_lock_irqsave(&drm->event_lock, flags);
223*dee8268fSThierry Reding 
224*dee8268fSThierry Reding 	if (dc->event && dc->event->base.file_priv == file) {
225*dee8268fSThierry Reding 		dc->event->base.destroy(&dc->event->base);
226*dee8268fSThierry Reding 		drm_vblank_put(drm, dc->pipe);
227*dee8268fSThierry Reding 		dc->event = NULL;
228*dee8268fSThierry Reding 	}
229*dee8268fSThierry Reding 
230*dee8268fSThierry Reding 	spin_unlock_irqrestore(&drm->event_lock, flags);
231*dee8268fSThierry Reding }
232*dee8268fSThierry Reding 
233*dee8268fSThierry Reding static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
234*dee8268fSThierry Reding 			      struct drm_pending_vblank_event *event, uint32_t page_flip_flags)
235*dee8268fSThierry Reding {
236*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
237*dee8268fSThierry Reding 	struct drm_device *drm = crtc->dev;
238*dee8268fSThierry Reding 
239*dee8268fSThierry Reding 	if (dc->event)
240*dee8268fSThierry Reding 		return -EBUSY;
241*dee8268fSThierry Reding 
242*dee8268fSThierry Reding 	if (event) {
243*dee8268fSThierry Reding 		event->pipe = dc->pipe;
244*dee8268fSThierry Reding 		dc->event = event;
245*dee8268fSThierry Reding 		drm_vblank_get(drm, dc->pipe);
246*dee8268fSThierry Reding 	}
247*dee8268fSThierry Reding 
248*dee8268fSThierry Reding 	tegra_dc_set_base(dc, 0, 0, fb);
249*dee8268fSThierry Reding 	crtc->fb = fb;
250*dee8268fSThierry Reding 
251*dee8268fSThierry Reding 	return 0;
252*dee8268fSThierry Reding }
253*dee8268fSThierry Reding 
254*dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
255*dee8268fSThierry Reding 	.page_flip = tegra_dc_page_flip,
256*dee8268fSThierry Reding 	.set_config = drm_crtc_helper_set_config,
257*dee8268fSThierry Reding 	.destroy = drm_crtc_cleanup,
258*dee8268fSThierry Reding };
259*dee8268fSThierry Reding 
260*dee8268fSThierry Reding static void tegra_crtc_disable(struct drm_crtc *crtc)
261*dee8268fSThierry Reding {
262*dee8268fSThierry Reding 	struct drm_device *drm = crtc->dev;
263*dee8268fSThierry Reding 	struct drm_plane *plane;
264*dee8268fSThierry Reding 
265*dee8268fSThierry Reding 	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
266*dee8268fSThierry Reding 		if (plane->crtc == crtc) {
267*dee8268fSThierry Reding 			tegra_plane_disable(plane);
268*dee8268fSThierry Reding 			plane->crtc = NULL;
269*dee8268fSThierry Reding 
270*dee8268fSThierry Reding 			if (plane->fb) {
271*dee8268fSThierry Reding 				drm_framebuffer_unreference(plane->fb);
272*dee8268fSThierry Reding 				plane->fb = NULL;
273*dee8268fSThierry Reding 			}
274*dee8268fSThierry Reding 		}
275*dee8268fSThierry Reding 	}
276*dee8268fSThierry Reding }
277*dee8268fSThierry Reding 
278*dee8268fSThierry Reding static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
279*dee8268fSThierry Reding 				  const struct drm_display_mode *mode,
280*dee8268fSThierry Reding 				  struct drm_display_mode *adjusted)
281*dee8268fSThierry Reding {
282*dee8268fSThierry Reding 	return true;
283*dee8268fSThierry Reding }
284*dee8268fSThierry Reding 
285*dee8268fSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
286*dee8268fSThierry Reding 				  unsigned int bpp)
287*dee8268fSThierry Reding {
288*dee8268fSThierry Reding 	fixed20_12 outf = dfixed_init(out);
289*dee8268fSThierry Reding 	fixed20_12 inf = dfixed_init(in);
290*dee8268fSThierry Reding 	u32 dda_inc;
291*dee8268fSThierry Reding 	int max;
292*dee8268fSThierry Reding 
293*dee8268fSThierry Reding 	if (v)
294*dee8268fSThierry Reding 		max = 15;
295*dee8268fSThierry Reding 	else {
296*dee8268fSThierry Reding 		switch (bpp) {
297*dee8268fSThierry Reding 		case 2:
298*dee8268fSThierry Reding 			max = 8;
299*dee8268fSThierry Reding 			break;
300*dee8268fSThierry Reding 
301*dee8268fSThierry Reding 		default:
302*dee8268fSThierry Reding 			WARN_ON_ONCE(1);
303*dee8268fSThierry Reding 			/* fallthrough */
304*dee8268fSThierry Reding 		case 4:
305*dee8268fSThierry Reding 			max = 4;
306*dee8268fSThierry Reding 			break;
307*dee8268fSThierry Reding 		}
308*dee8268fSThierry Reding 	}
309*dee8268fSThierry Reding 
310*dee8268fSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
311*dee8268fSThierry Reding 	inf.full -= dfixed_const(1);
312*dee8268fSThierry Reding 
313*dee8268fSThierry Reding 	dda_inc = dfixed_div(inf, outf);
314*dee8268fSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
315*dee8268fSThierry Reding 
316*dee8268fSThierry Reding 	return dda_inc;
317*dee8268fSThierry Reding }
318*dee8268fSThierry Reding 
319*dee8268fSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
320*dee8268fSThierry Reding {
321*dee8268fSThierry Reding 	fixed20_12 inf = dfixed_init(in);
322*dee8268fSThierry Reding 	return dfixed_frac(inf);
323*dee8268fSThierry Reding }
324*dee8268fSThierry Reding 
325*dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
326*dee8268fSThierry Reding 				struct drm_display_mode *mode)
327*dee8268fSThierry Reding {
328*dee8268fSThierry Reding 	/* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */
329*dee8268fSThierry Reding 	unsigned int h_ref_to_sync = 0;
330*dee8268fSThierry Reding 	unsigned int v_ref_to_sync = 0;
331*dee8268fSThierry Reding 	unsigned long value;
332*dee8268fSThierry Reding 
333*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
334*dee8268fSThierry Reding 
335*dee8268fSThierry Reding 	value = (v_ref_to_sync << 16) | h_ref_to_sync;
336*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
337*dee8268fSThierry Reding 
338*dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
339*dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
340*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
341*dee8268fSThierry Reding 
342*dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
343*dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
344*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
345*dee8268fSThierry Reding 
346*dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
347*dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
348*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
349*dee8268fSThierry Reding 
350*dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
351*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
352*dee8268fSThierry Reding 
353*dee8268fSThierry Reding 	return 0;
354*dee8268fSThierry Reding }
355*dee8268fSThierry Reding 
356*dee8268fSThierry Reding static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
357*dee8268fSThierry Reding 				struct drm_display_mode *mode,
358*dee8268fSThierry Reding 				unsigned long *div)
359*dee8268fSThierry Reding {
360*dee8268fSThierry Reding 	unsigned long pclk = mode->clock * 1000, rate;
361*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
362*dee8268fSThierry Reding 	struct tegra_output *output = NULL;
363*dee8268fSThierry Reding 	struct drm_encoder *encoder;
364*dee8268fSThierry Reding 	long err;
365*dee8268fSThierry Reding 
366*dee8268fSThierry Reding 	list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
367*dee8268fSThierry Reding 		if (encoder->crtc == crtc) {
368*dee8268fSThierry Reding 			output = encoder_to_output(encoder);
369*dee8268fSThierry Reding 			break;
370*dee8268fSThierry Reding 		}
371*dee8268fSThierry Reding 
372*dee8268fSThierry Reding 	if (!output)
373*dee8268fSThierry Reding 		return -ENODEV;
374*dee8268fSThierry Reding 
375*dee8268fSThierry Reding 	/*
376*dee8268fSThierry Reding 	 * This assumes that the display controller will divide its parent
377*dee8268fSThierry Reding 	 * clock by 2 to generate the pixel clock.
378*dee8268fSThierry Reding 	 */
379*dee8268fSThierry Reding 	err = tegra_output_setup_clock(output, dc->clk, pclk * 2);
380*dee8268fSThierry Reding 	if (err < 0) {
381*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to setup clock: %ld\n", err);
382*dee8268fSThierry Reding 		return err;
383*dee8268fSThierry Reding 	}
384*dee8268fSThierry Reding 
385*dee8268fSThierry Reding 	rate = clk_get_rate(dc->clk);
386*dee8268fSThierry Reding 	*div = (rate * 2 / pclk) - 2;
387*dee8268fSThierry Reding 
388*dee8268fSThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div);
389*dee8268fSThierry Reding 
390*dee8268fSThierry Reding 	return 0;
391*dee8268fSThierry Reding }
392*dee8268fSThierry Reding 
393*dee8268fSThierry Reding static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
394*dee8268fSThierry Reding {
395*dee8268fSThierry Reding 	switch (format) {
396*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422:
397*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YUV422:
398*dee8268fSThierry Reding 		if (planar)
399*dee8268fSThierry Reding 			*planar = false;
400*dee8268fSThierry Reding 
401*dee8268fSThierry Reding 		return true;
402*dee8268fSThierry Reding 
403*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YCbCr420P:
404*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YUV420P:
405*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422P:
406*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YUV422P:
407*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422R:
408*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YUV422R:
409*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422RA:
410*dee8268fSThierry Reding 	case WIN_COLOR_DEPTH_YUV422RA:
411*dee8268fSThierry Reding 		if (planar)
412*dee8268fSThierry Reding 			*planar = true;
413*dee8268fSThierry Reding 
414*dee8268fSThierry Reding 		return true;
415*dee8268fSThierry Reding 	}
416*dee8268fSThierry Reding 
417*dee8268fSThierry Reding 	return false;
418*dee8268fSThierry Reding }
419*dee8268fSThierry Reding 
420*dee8268fSThierry Reding int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
421*dee8268fSThierry Reding 			  const struct tegra_dc_window *window)
422*dee8268fSThierry Reding {
423*dee8268fSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
424*dee8268fSThierry Reding 	unsigned long value;
425*dee8268fSThierry Reding 	bool yuv, planar;
426*dee8268fSThierry Reding 
427*dee8268fSThierry Reding 	/*
428*dee8268fSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
429*dee8268fSThierry Reding 	 * account only the luma component and therefore is 1.
430*dee8268fSThierry Reding 	 */
431*dee8268fSThierry Reding 	yuv = tegra_dc_format_is_yuv(window->format, &planar);
432*dee8268fSThierry Reding 	if (!yuv)
433*dee8268fSThierry Reding 		bpp = window->bits_per_pixel / 8;
434*dee8268fSThierry Reding 	else
435*dee8268fSThierry Reding 		bpp = planar ? 1 : 2;
436*dee8268fSThierry Reding 
437*dee8268fSThierry Reding 	value = WINDOW_A_SELECT << index;
438*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
439*dee8268fSThierry Reding 
440*dee8268fSThierry Reding 	tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
441*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
442*dee8268fSThierry Reding 
443*dee8268fSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
444*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_POSITION);
445*dee8268fSThierry Reding 
446*dee8268fSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
447*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_SIZE);
448*dee8268fSThierry Reding 
449*dee8268fSThierry Reding 	h_offset = window->src.x * bpp;
450*dee8268fSThierry Reding 	v_offset = window->src.y;
451*dee8268fSThierry Reding 	h_size = window->src.w * bpp;
452*dee8268fSThierry Reding 	v_size = window->src.h;
453*dee8268fSThierry Reding 
454*dee8268fSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
455*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
456*dee8268fSThierry Reding 
457*dee8268fSThierry Reding 	/*
458*dee8268fSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
459*dee8268fSThierry Reding 	 * modes needs to take into account all Y, U and V components.
460*dee8268fSThierry Reding 	 */
461*dee8268fSThierry Reding 	if (yuv && planar)
462*dee8268fSThierry Reding 		bpp = 2;
463*dee8268fSThierry Reding 
464*dee8268fSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
465*dee8268fSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
466*dee8268fSThierry Reding 
467*dee8268fSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
468*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
469*dee8268fSThierry Reding 
470*dee8268fSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
471*dee8268fSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
472*dee8268fSThierry Reding 
473*dee8268fSThierry Reding 	tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
474*dee8268fSThierry Reding 	tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
475*dee8268fSThierry Reding 
476*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
477*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
478*dee8268fSThierry Reding 
479*dee8268fSThierry Reding 	tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
480*dee8268fSThierry Reding 
481*dee8268fSThierry Reding 	if (yuv && planar) {
482*dee8268fSThierry Reding 		tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
483*dee8268fSThierry Reding 		tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
484*dee8268fSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
485*dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
486*dee8268fSThierry Reding 	} else {
487*dee8268fSThierry Reding 		tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
488*dee8268fSThierry Reding 	}
489*dee8268fSThierry Reding 
490*dee8268fSThierry Reding 	tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
491*dee8268fSThierry Reding 	tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
492*dee8268fSThierry Reding 
493*dee8268fSThierry Reding 	value = WIN_ENABLE;
494*dee8268fSThierry Reding 
495*dee8268fSThierry Reding 	if (yuv) {
496*dee8268fSThierry Reding 		/* setup default colorspace conversion coefficients */
497*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
498*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
499*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
500*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
501*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
502*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
503*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
504*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
505*dee8268fSThierry Reding 
506*dee8268fSThierry Reding 		value |= CSC_ENABLE;
507*dee8268fSThierry Reding 	} else if (window->bits_per_pixel < 24) {
508*dee8268fSThierry Reding 		value |= COLOR_EXPAND;
509*dee8268fSThierry Reding 	}
510*dee8268fSThierry Reding 
511*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
512*dee8268fSThierry Reding 
513*dee8268fSThierry Reding 	/*
514*dee8268fSThierry Reding 	 * Disable blending and assume Window A is the bottom-most window,
515*dee8268fSThierry Reding 	 * Window C is the top-most window and Window B is in the middle.
516*dee8268fSThierry Reding 	 */
517*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
518*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
519*dee8268fSThierry Reding 
520*dee8268fSThierry Reding 	switch (index) {
521*dee8268fSThierry Reding 	case 0:
522*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
523*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
524*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
525*dee8268fSThierry Reding 		break;
526*dee8268fSThierry Reding 
527*dee8268fSThierry Reding 	case 1:
528*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
529*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
530*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
531*dee8268fSThierry Reding 		break;
532*dee8268fSThierry Reding 
533*dee8268fSThierry Reding 	case 2:
534*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
535*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
536*dee8268fSThierry Reding 		tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
537*dee8268fSThierry Reding 		break;
538*dee8268fSThierry Reding 	}
539*dee8268fSThierry Reding 
540*dee8268fSThierry Reding 	tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
541*dee8268fSThierry Reding 	tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
542*dee8268fSThierry Reding 
543*dee8268fSThierry Reding 	return 0;
544*dee8268fSThierry Reding }
545*dee8268fSThierry Reding 
546*dee8268fSThierry Reding unsigned int tegra_dc_format(uint32_t format)
547*dee8268fSThierry Reding {
548*dee8268fSThierry Reding 	switch (format) {
549*dee8268fSThierry Reding 	case DRM_FORMAT_XBGR8888:
550*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_R8G8B8A8;
551*dee8268fSThierry Reding 
552*dee8268fSThierry Reding 	case DRM_FORMAT_XRGB8888:
553*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_B8G8R8A8;
554*dee8268fSThierry Reding 
555*dee8268fSThierry Reding 	case DRM_FORMAT_RGB565:
556*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_B5G6R5;
557*dee8268fSThierry Reding 
558*dee8268fSThierry Reding 	case DRM_FORMAT_UYVY:
559*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_YCbCr422;
560*dee8268fSThierry Reding 
561*dee8268fSThierry Reding 	case DRM_FORMAT_YUV420:
562*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_YCbCr420P;
563*dee8268fSThierry Reding 
564*dee8268fSThierry Reding 	case DRM_FORMAT_YUV422:
565*dee8268fSThierry Reding 		return WIN_COLOR_DEPTH_YCbCr422P;
566*dee8268fSThierry Reding 
567*dee8268fSThierry Reding 	default:
568*dee8268fSThierry Reding 		break;
569*dee8268fSThierry Reding 	}
570*dee8268fSThierry Reding 
571*dee8268fSThierry Reding 	WARN(1, "unsupported pixel format %u, using default\n", format);
572*dee8268fSThierry Reding 	return WIN_COLOR_DEPTH_B8G8R8A8;
573*dee8268fSThierry Reding }
574*dee8268fSThierry Reding 
575*dee8268fSThierry Reding static int tegra_crtc_mode_set(struct drm_crtc *crtc,
576*dee8268fSThierry Reding 			       struct drm_display_mode *mode,
577*dee8268fSThierry Reding 			       struct drm_display_mode *adjusted,
578*dee8268fSThierry Reding 			       int x, int y, struct drm_framebuffer *old_fb)
579*dee8268fSThierry Reding {
580*dee8268fSThierry Reding 	struct tegra_bo *bo = tegra_fb_get_plane(crtc->fb, 0);
581*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
582*dee8268fSThierry Reding 	struct tegra_dc_window window;
583*dee8268fSThierry Reding 	unsigned long div, value;
584*dee8268fSThierry Reding 	int err;
585*dee8268fSThierry Reding 
586*dee8268fSThierry Reding 	drm_vblank_pre_modeset(crtc->dev, dc->pipe);
587*dee8268fSThierry Reding 
588*dee8268fSThierry Reding 	err = tegra_crtc_setup_clk(crtc, mode, &div);
589*dee8268fSThierry Reding 	if (err) {
590*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
591*dee8268fSThierry Reding 		return err;
592*dee8268fSThierry Reding 	}
593*dee8268fSThierry Reding 
594*dee8268fSThierry Reding 	/* program display mode */
595*dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
596*dee8268fSThierry Reding 
597*dee8268fSThierry Reding 	value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
598*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
599*dee8268fSThierry Reding 
600*dee8268fSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY(1));
601*dee8268fSThierry Reding 	value &= ~LVS_OUTPUT_POLARITY_LOW;
602*dee8268fSThierry Reding 	value &= ~LHS_OUTPUT_POLARITY_LOW;
603*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
604*dee8268fSThierry Reding 
605*dee8268fSThierry Reding 	value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |
606*dee8268fSThierry Reding 		DISP_ORDER_RED_BLUE;
607*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_INTERFACE_CONTROL);
608*dee8268fSThierry Reding 
609*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0x00010001, DC_DISP_SHIFT_CLOCK_OPTIONS);
610*dee8268fSThierry Reding 
611*dee8268fSThierry Reding 	value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
612*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
613*dee8268fSThierry Reding 
614*dee8268fSThierry Reding 	/* setup window parameters */
615*dee8268fSThierry Reding 	memset(&window, 0, sizeof(window));
616*dee8268fSThierry Reding 	window.src.x = 0;
617*dee8268fSThierry Reding 	window.src.y = 0;
618*dee8268fSThierry Reding 	window.src.w = mode->hdisplay;
619*dee8268fSThierry Reding 	window.src.h = mode->vdisplay;
620*dee8268fSThierry Reding 	window.dst.x = 0;
621*dee8268fSThierry Reding 	window.dst.y = 0;
622*dee8268fSThierry Reding 	window.dst.w = mode->hdisplay;
623*dee8268fSThierry Reding 	window.dst.h = mode->vdisplay;
624*dee8268fSThierry Reding 	window.format = tegra_dc_format(crtc->fb->pixel_format);
625*dee8268fSThierry Reding 	window.bits_per_pixel = crtc->fb->bits_per_pixel;
626*dee8268fSThierry Reding 	window.stride[0] = crtc->fb->pitches[0];
627*dee8268fSThierry Reding 	window.base[0] = bo->paddr;
628*dee8268fSThierry Reding 
629*dee8268fSThierry Reding 	err = tegra_dc_setup_window(dc, 0, &window);
630*dee8268fSThierry Reding 	if (err < 0)
631*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to enable root plane\n");
632*dee8268fSThierry Reding 
633*dee8268fSThierry Reding 	return 0;
634*dee8268fSThierry Reding }
635*dee8268fSThierry Reding 
636*dee8268fSThierry Reding static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
637*dee8268fSThierry Reding 				    struct drm_framebuffer *old_fb)
638*dee8268fSThierry Reding {
639*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
640*dee8268fSThierry Reding 
641*dee8268fSThierry Reding 	return tegra_dc_set_base(dc, x, y, crtc->fb);
642*dee8268fSThierry Reding }
643*dee8268fSThierry Reding 
644*dee8268fSThierry Reding static void tegra_crtc_prepare(struct drm_crtc *crtc)
645*dee8268fSThierry Reding {
646*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
647*dee8268fSThierry Reding 	unsigned int syncpt;
648*dee8268fSThierry Reding 	unsigned long value;
649*dee8268fSThierry Reding 
650*dee8268fSThierry Reding 	/* hardware initialization */
651*dee8268fSThierry Reding 	tegra_periph_reset_deassert(dc->clk);
652*dee8268fSThierry Reding 	usleep_range(10000, 20000);
653*dee8268fSThierry Reding 
654*dee8268fSThierry Reding 	if (dc->pipe)
655*dee8268fSThierry Reding 		syncpt = SYNCPT_VBLANK1;
656*dee8268fSThierry Reding 	else
657*dee8268fSThierry Reding 		syncpt = SYNCPT_VBLANK0;
658*dee8268fSThierry Reding 
659*dee8268fSThierry Reding 	/* initialize display controller */
660*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
661*dee8268fSThierry Reding 	tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
662*dee8268fSThierry Reding 
663*dee8268fSThierry Reding 	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
664*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
665*dee8268fSThierry Reding 
666*dee8268fSThierry Reding 	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
667*dee8268fSThierry Reding 		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
668*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
669*dee8268fSThierry Reding 
670*dee8268fSThierry Reding 	value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
671*dee8268fSThierry Reding 		PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
672*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
673*dee8268fSThierry Reding 
674*dee8268fSThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
675*dee8268fSThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
676*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
677*dee8268fSThierry Reding 
678*dee8268fSThierry Reding 	/* initialize timer */
679*dee8268fSThierry Reding 	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
680*dee8268fSThierry Reding 		WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
681*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
682*dee8268fSThierry Reding 
683*dee8268fSThierry Reding 	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
684*dee8268fSThierry Reding 		WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
685*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
686*dee8268fSThierry Reding 
687*dee8268fSThierry Reding 	value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
688*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
689*dee8268fSThierry Reding 
690*dee8268fSThierry Reding 	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
691*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
692*dee8268fSThierry Reding }
693*dee8268fSThierry Reding 
694*dee8268fSThierry Reding static void tegra_crtc_commit(struct drm_crtc *crtc)
695*dee8268fSThierry Reding {
696*dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
697*dee8268fSThierry Reding 	unsigned long value;
698*dee8268fSThierry Reding 
699*dee8268fSThierry Reding 	value = GENERAL_UPDATE | WIN_A_UPDATE;
700*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
701*dee8268fSThierry Reding 
702*dee8268fSThierry Reding 	value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
703*dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
704*dee8268fSThierry Reding 
705*dee8268fSThierry Reding 	drm_vblank_post_modeset(crtc->dev, dc->pipe);
706*dee8268fSThierry Reding }
707*dee8268fSThierry Reding 
708*dee8268fSThierry Reding static void tegra_crtc_load_lut(struct drm_crtc *crtc)
709*dee8268fSThierry Reding {
710*dee8268fSThierry Reding }
711*dee8268fSThierry Reding 
712*dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
713*dee8268fSThierry Reding 	.disable = tegra_crtc_disable,
714*dee8268fSThierry Reding 	.mode_fixup = tegra_crtc_mode_fixup,
715*dee8268fSThierry Reding 	.mode_set = tegra_crtc_mode_set,
716*dee8268fSThierry Reding 	.mode_set_base = tegra_crtc_mode_set_base,
717*dee8268fSThierry Reding 	.prepare = tegra_crtc_prepare,
718*dee8268fSThierry Reding 	.commit = tegra_crtc_commit,
719*dee8268fSThierry Reding 	.load_lut = tegra_crtc_load_lut,
720*dee8268fSThierry Reding };
721*dee8268fSThierry Reding 
722*dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data)
723*dee8268fSThierry Reding {
724*dee8268fSThierry Reding 	struct tegra_dc *dc = data;
725*dee8268fSThierry Reding 	unsigned long status;
726*dee8268fSThierry Reding 
727*dee8268fSThierry Reding 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
728*dee8268fSThierry Reding 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
729*dee8268fSThierry Reding 
730*dee8268fSThierry Reding 	if (status & FRAME_END_INT) {
731*dee8268fSThierry Reding 		/*
732*dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
733*dee8268fSThierry Reding 		*/
734*dee8268fSThierry Reding 	}
735*dee8268fSThierry Reding 
736*dee8268fSThierry Reding 	if (status & VBLANK_INT) {
737*dee8268fSThierry Reding 		/*
738*dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
739*dee8268fSThierry Reding 		*/
740*dee8268fSThierry Reding 		drm_handle_vblank(dc->base.dev, dc->pipe);
741*dee8268fSThierry Reding 		tegra_dc_finish_page_flip(dc);
742*dee8268fSThierry Reding 	}
743*dee8268fSThierry Reding 
744*dee8268fSThierry Reding 	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
745*dee8268fSThierry Reding 		/*
746*dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
747*dee8268fSThierry Reding 		*/
748*dee8268fSThierry Reding 	}
749*dee8268fSThierry Reding 
750*dee8268fSThierry Reding 	return IRQ_HANDLED;
751*dee8268fSThierry Reding }
752*dee8268fSThierry Reding 
753*dee8268fSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
754*dee8268fSThierry Reding {
755*dee8268fSThierry Reding 	struct drm_info_node *node = s->private;
756*dee8268fSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
757*dee8268fSThierry Reding 
758*dee8268fSThierry Reding #define DUMP_REG(name)						\
759*dee8268fSThierry Reding 	seq_printf(s, "%-40s %#05x %08lx\n", #name, name,	\
760*dee8268fSThierry Reding 		   tegra_dc_readl(dc, name))
761*dee8268fSThierry Reding 
762*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
763*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
764*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR);
765*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT);
766*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL);
767*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR);
768*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT);
769*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL);
770*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR);
771*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT);
772*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL);
773*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR);
774*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC);
775*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
776*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_DISPLAY_COMMAND);
777*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_SIGNAL_RAISE);
778*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_DISPLAY_POWER_CONTROL);
779*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_INT_STATUS);
780*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_INT_MASK);
781*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_INT_ENABLE);
782*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_INT_TYPE);
783*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_INT_POLARITY);
784*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_SIGNAL_RAISE1);
785*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_SIGNAL_RAISE2);
786*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_SIGNAL_RAISE3);
787*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_STATE_ACCESS);
788*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_STATE_CONTROL);
789*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
790*dee8268fSThierry Reding 	DUMP_REG(DC_CMD_REG_ACT_CONTROL);
791*dee8268fSThierry Reding 	DUMP_REG(DC_COM_CRC_CONTROL);
792*dee8268fSThierry Reding 	DUMP_REG(DC_COM_CRC_CHECKSUM);
793*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(0));
794*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(1));
795*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(2));
796*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(3));
797*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(0));
798*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(1));
799*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(2));
800*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(3));
801*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_DATA(0));
802*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_DATA(1));
803*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_DATA(2));
804*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_DATA(3));
805*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_ENABLE(0));
806*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_ENABLE(1));
807*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_ENABLE(2));
808*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_ENABLE(3));
809*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_DATA(0));
810*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_INPUT_DATA(1));
811*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(0));
812*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(1));
813*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(2));
814*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(3));
815*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(4));
816*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(5));
817*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(6));
818*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_MISC_CONTROL);
819*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_PM0_CONTROL);
820*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_PM0_DUTY_CYCLE);
821*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_PM1_CONTROL);
822*dee8268fSThierry Reding 	DUMP_REG(DC_COM_PIN_PM1_DUTY_CYCLE);
823*dee8268fSThierry Reding 	DUMP_REG(DC_COM_SPI_CONTROL);
824*dee8268fSThierry Reding 	DUMP_REG(DC_COM_SPI_START_BYTE);
825*dee8268fSThierry Reding 	DUMP_REG(DC_COM_HSPI_WRITE_DATA_AB);
826*dee8268fSThierry Reding 	DUMP_REG(DC_COM_HSPI_WRITE_DATA_CD);
827*dee8268fSThierry Reding 	DUMP_REG(DC_COM_HSPI_CS_DC);
828*dee8268fSThierry Reding 	DUMP_REG(DC_COM_SCRATCH_REGISTER_A);
829*dee8268fSThierry Reding 	DUMP_REG(DC_COM_SCRATCH_REGISTER_B);
830*dee8268fSThierry Reding 	DUMP_REG(DC_COM_GPIO_CTRL);
831*dee8268fSThierry Reding 	DUMP_REG(DC_COM_GPIO_DEBOUNCE_COUNTER);
832*dee8268fSThierry Reding 	DUMP_REG(DC_COM_CRC_CHECKSUM_LATCHED);
833*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0);
834*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1);
835*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_WIN_OPTIONS);
836*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY);
837*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
838*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS);
839*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_REF_TO_SYNC);
840*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SYNC_WIDTH);
841*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_BACK_PORCH);
842*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_ACTIVE);
843*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_FRONT_PORCH);
844*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE0_CONTROL);
845*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE0_POSITION_A);
846*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE0_POSITION_B);
847*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE0_POSITION_C);
848*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE0_POSITION_D);
849*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE1_CONTROL);
850*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE1_POSITION_A);
851*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE1_POSITION_B);
852*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE1_POSITION_C);
853*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE1_POSITION_D);
854*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE2_CONTROL);
855*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE2_POSITION_A);
856*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE2_POSITION_B);
857*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE2_POSITION_C);
858*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_H_PULSE2_POSITION_D);
859*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE0_CONTROL);
860*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE0_POSITION_A);
861*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE0_POSITION_B);
862*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE0_POSITION_C);
863*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE1_CONTROL);
864*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE1_POSITION_A);
865*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE1_POSITION_B);
866*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE1_POSITION_C);
867*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE2_CONTROL);
868*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE2_POSITION_A);
869*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE3_CONTROL);
870*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_V_PULSE3_POSITION_A);
871*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_M0_CONTROL);
872*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_M1_CONTROL);
873*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DI_CONTROL);
874*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_PP_CONTROL);
875*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_PP_SELECT_A);
876*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_PP_SELECT_B);
877*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_PP_SELECT_C);
878*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_PP_SELECT_D);
879*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL);
880*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL);
881*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_COLOR_CONTROL);
882*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS);
883*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
884*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
885*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
886*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_BORDER_COLOR);
887*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
888*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
889*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
890*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_COLOR_KEY1_UPPER);
891*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_FOREGROUND);
892*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_BACKGROUND);
893*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_START_ADDR);
894*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS);
895*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_POSITION);
896*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_CURSOR_POSITION_NS);
897*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_INIT_SEQ_CONTROL);
898*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A);
899*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B);
900*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C);
901*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D);
902*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL);
903*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST);
904*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST);
905*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_MCCIF_DISPLAY1A_HYST);
906*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST);
907*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DAC_CRT_CTRL);
908*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DISP_MISC_CONTROL);
909*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_CONTROL);
910*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_CSC_COEFF);
911*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(0));
912*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(1));
913*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(2));
914*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(3));
915*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(4));
916*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(5));
917*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(6));
918*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(7));
919*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_LUT(8));
920*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_FLICKER_CONTROL);
921*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_DC_PIXEL_COUNT);
922*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(0));
923*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(1));
924*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(2));
925*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(3));
926*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(4));
927*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(5));
928*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(6));
929*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HISTOGRAM(7));
930*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_BL_TF(0));
931*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_BL_TF(1));
932*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_BL_TF(2));
933*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_BL_TF(3));
934*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_BL_CONTROL);
935*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_HW_K_VALUES);
936*dee8268fSThierry Reding 	DUMP_REG(DC_DISP_SD_MAN_K_VALUES);
937*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_WIN_OPTIONS);
938*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BYTE_SWAP);
939*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BUFFER_CONTROL);
940*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_COLOR_DEPTH);
941*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_POSITION);
942*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_SIZE);
943*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_PRESCALED_SIZE);
944*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_H_INITIAL_DDA);
945*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_V_INITIAL_DDA);
946*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_DDA_INC);
947*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_LINE_STRIDE);
948*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BUF_STRIDE);
949*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_UV_BUF_STRIDE);
950*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BUFFER_ADDR_MODE);
951*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_DV_CONTROL);
952*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BLEND_NOKEY);
953*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BLEND_1WIN);
954*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BLEND_2WIN_X);
955*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BLEND_2WIN_Y);
956*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_BLEND_3WIN_XY);
957*dee8268fSThierry Reding 	DUMP_REG(DC_WIN_HP_FETCH_CONTROL);
958*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR);
959*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR_NS);
960*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR_U);
961*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR_U_NS);
962*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR_V);
963*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_START_ADDR_V_NS);
964*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_ADDR_H_OFFSET);
965*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_ADDR_H_OFFSET_NS);
966*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_ADDR_V_OFFSET);
967*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_ADDR_V_OFFSET_NS);
968*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_UFLOW_STATUS);
969*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_AD_UFLOW_STATUS);
970*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_BD_UFLOW_STATUS);
971*dee8268fSThierry Reding 	DUMP_REG(DC_WINBUF_CD_UFLOW_STATUS);
972*dee8268fSThierry Reding 
973*dee8268fSThierry Reding #undef DUMP_REG
974*dee8268fSThierry Reding 
975*dee8268fSThierry Reding 	return 0;
976*dee8268fSThierry Reding }
977*dee8268fSThierry Reding 
978*dee8268fSThierry Reding static struct drm_info_list debugfs_files[] = {
979*dee8268fSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
980*dee8268fSThierry Reding };
981*dee8268fSThierry Reding 
982*dee8268fSThierry Reding static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor)
983*dee8268fSThierry Reding {
984*dee8268fSThierry Reding 	unsigned int i;
985*dee8268fSThierry Reding 	char *name;
986*dee8268fSThierry Reding 	int err;
987*dee8268fSThierry Reding 
988*dee8268fSThierry Reding 	name = kasprintf(GFP_KERNEL, "dc.%d", dc->pipe);
989*dee8268fSThierry Reding 	dc->debugfs = debugfs_create_dir(name, minor->debugfs_root);
990*dee8268fSThierry Reding 	kfree(name);
991*dee8268fSThierry Reding 
992*dee8268fSThierry Reding 	if (!dc->debugfs)
993*dee8268fSThierry Reding 		return -ENOMEM;
994*dee8268fSThierry Reding 
995*dee8268fSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
996*dee8268fSThierry Reding 				    GFP_KERNEL);
997*dee8268fSThierry Reding 	if (!dc->debugfs_files) {
998*dee8268fSThierry Reding 		err = -ENOMEM;
999*dee8268fSThierry Reding 		goto remove;
1000*dee8268fSThierry Reding 	}
1001*dee8268fSThierry Reding 
1002*dee8268fSThierry Reding 	for (i = 0; i < ARRAY_SIZE(debugfs_files); i++)
1003*dee8268fSThierry Reding 		dc->debugfs_files[i].data = dc;
1004*dee8268fSThierry Reding 
1005*dee8268fSThierry Reding 	err = drm_debugfs_create_files(dc->debugfs_files,
1006*dee8268fSThierry Reding 				       ARRAY_SIZE(debugfs_files),
1007*dee8268fSThierry Reding 				       dc->debugfs, minor);
1008*dee8268fSThierry Reding 	if (err < 0)
1009*dee8268fSThierry Reding 		goto free;
1010*dee8268fSThierry Reding 
1011*dee8268fSThierry Reding 	dc->minor = minor;
1012*dee8268fSThierry Reding 
1013*dee8268fSThierry Reding 	return 0;
1014*dee8268fSThierry Reding 
1015*dee8268fSThierry Reding free:
1016*dee8268fSThierry Reding 	kfree(dc->debugfs_files);
1017*dee8268fSThierry Reding 	dc->debugfs_files = NULL;
1018*dee8268fSThierry Reding remove:
1019*dee8268fSThierry Reding 	debugfs_remove(dc->debugfs);
1020*dee8268fSThierry Reding 	dc->debugfs = NULL;
1021*dee8268fSThierry Reding 
1022*dee8268fSThierry Reding 	return err;
1023*dee8268fSThierry Reding }
1024*dee8268fSThierry Reding 
1025*dee8268fSThierry Reding static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
1026*dee8268fSThierry Reding {
1027*dee8268fSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files),
1028*dee8268fSThierry Reding 				 dc->minor);
1029*dee8268fSThierry Reding 	dc->minor = NULL;
1030*dee8268fSThierry Reding 
1031*dee8268fSThierry Reding 	kfree(dc->debugfs_files);
1032*dee8268fSThierry Reding 	dc->debugfs_files = NULL;
1033*dee8268fSThierry Reding 
1034*dee8268fSThierry Reding 	debugfs_remove(dc->debugfs);
1035*dee8268fSThierry Reding 	dc->debugfs = NULL;
1036*dee8268fSThierry Reding 
1037*dee8268fSThierry Reding 	return 0;
1038*dee8268fSThierry Reding }
1039*dee8268fSThierry Reding 
1040*dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client)
1041*dee8268fSThierry Reding {
1042*dee8268fSThierry Reding 	struct tegra_drm *tegra = dev_get_drvdata(client->parent);
1043*dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
1044*dee8268fSThierry Reding 	int err;
1045*dee8268fSThierry Reding 
1046*dee8268fSThierry Reding 	dc->pipe = tegra->drm->mode_config.num_crtc;
1047*dee8268fSThierry Reding 
1048*dee8268fSThierry Reding 	drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
1049*dee8268fSThierry Reding 	drm_mode_crtc_set_gamma_size(&dc->base, 256);
1050*dee8268fSThierry Reding 	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
1051*dee8268fSThierry Reding 
1052*dee8268fSThierry Reding 	err = tegra_dc_rgb_init(tegra->drm, dc);
1053*dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
1054*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
1055*dee8268fSThierry Reding 		return err;
1056*dee8268fSThierry Reding 	}
1057*dee8268fSThierry Reding 
1058*dee8268fSThierry Reding 	err = tegra_dc_add_planes(tegra->drm, dc);
1059*dee8268fSThierry Reding 	if (err < 0)
1060*dee8268fSThierry Reding 		return err;
1061*dee8268fSThierry Reding 
1062*dee8268fSThierry Reding 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
1063*dee8268fSThierry Reding 		err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
1064*dee8268fSThierry Reding 		if (err < 0)
1065*dee8268fSThierry Reding 			dev_err(dc->dev, "debugfs setup failed: %d\n", err);
1066*dee8268fSThierry Reding 	}
1067*dee8268fSThierry Reding 
1068*dee8268fSThierry Reding 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
1069*dee8268fSThierry Reding 			       dev_name(dc->dev), dc);
1070*dee8268fSThierry Reding 	if (err < 0) {
1071*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
1072*dee8268fSThierry Reding 			err);
1073*dee8268fSThierry Reding 		return err;
1074*dee8268fSThierry Reding 	}
1075*dee8268fSThierry Reding 
1076*dee8268fSThierry Reding 	return 0;
1077*dee8268fSThierry Reding }
1078*dee8268fSThierry Reding 
1079*dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client)
1080*dee8268fSThierry Reding {
1081*dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
1082*dee8268fSThierry Reding 	int err;
1083*dee8268fSThierry Reding 
1084*dee8268fSThierry Reding 	devm_free_irq(dc->dev, dc->irq, dc);
1085*dee8268fSThierry Reding 
1086*dee8268fSThierry Reding 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
1087*dee8268fSThierry Reding 		err = tegra_dc_debugfs_exit(dc);
1088*dee8268fSThierry Reding 		if (err < 0)
1089*dee8268fSThierry Reding 			dev_err(dc->dev, "debugfs cleanup failed: %d\n", err);
1090*dee8268fSThierry Reding 	}
1091*dee8268fSThierry Reding 
1092*dee8268fSThierry Reding 	err = tegra_dc_rgb_exit(dc);
1093*dee8268fSThierry Reding 	if (err) {
1094*dee8268fSThierry Reding 		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
1095*dee8268fSThierry Reding 		return err;
1096*dee8268fSThierry Reding 	}
1097*dee8268fSThierry Reding 
1098*dee8268fSThierry Reding 	return 0;
1099*dee8268fSThierry Reding }
1100*dee8268fSThierry Reding 
1101*dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = {
1102*dee8268fSThierry Reding 	.init = tegra_dc_init,
1103*dee8268fSThierry Reding 	.exit = tegra_dc_exit,
1104*dee8268fSThierry Reding };
1105*dee8268fSThierry Reding 
1106*dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev)
1107*dee8268fSThierry Reding {
1108*dee8268fSThierry Reding 	struct resource *regs;
1109*dee8268fSThierry Reding 	struct tegra_dc *dc;
1110*dee8268fSThierry Reding 	int err;
1111*dee8268fSThierry Reding 
1112*dee8268fSThierry Reding 	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
1113*dee8268fSThierry Reding 	if (!dc)
1114*dee8268fSThierry Reding 		return -ENOMEM;
1115*dee8268fSThierry Reding 
1116*dee8268fSThierry Reding 	spin_lock_init(&dc->lock);
1117*dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->list);
1118*dee8268fSThierry Reding 	dc->dev = &pdev->dev;
1119*dee8268fSThierry Reding 
1120*dee8268fSThierry Reding 	dc->clk = devm_clk_get(&pdev->dev, NULL);
1121*dee8268fSThierry Reding 	if (IS_ERR(dc->clk)) {
1122*dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get clock\n");
1123*dee8268fSThierry Reding 		return PTR_ERR(dc->clk);
1124*dee8268fSThierry Reding 	}
1125*dee8268fSThierry Reding 
1126*dee8268fSThierry Reding 	err = clk_prepare_enable(dc->clk);
1127*dee8268fSThierry Reding 	if (err < 0)
1128*dee8268fSThierry Reding 		return err;
1129*dee8268fSThierry Reding 
1130*dee8268fSThierry Reding 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1131*dee8268fSThierry Reding 	dc->regs = devm_ioremap_resource(&pdev->dev, regs);
1132*dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
1133*dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
1134*dee8268fSThierry Reding 
1135*dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
1136*dee8268fSThierry Reding 	if (dc->irq < 0) {
1137*dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get IRQ\n");
1138*dee8268fSThierry Reding 		return -ENXIO;
1139*dee8268fSThierry Reding 	}
1140*dee8268fSThierry Reding 
1141*dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
1142*dee8268fSThierry Reding 	dc->client.ops = &dc_client_ops;
1143*dee8268fSThierry Reding 	dc->client.dev = &pdev->dev;
1144*dee8268fSThierry Reding 
1145*dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
1146*dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
1147*dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err);
1148*dee8268fSThierry Reding 		return err;
1149*dee8268fSThierry Reding 	}
1150*dee8268fSThierry Reding 
1151*dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
1152*dee8268fSThierry Reding 	if (err < 0) {
1153*dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
1154*dee8268fSThierry Reding 			err);
1155*dee8268fSThierry Reding 		return err;
1156*dee8268fSThierry Reding 	}
1157*dee8268fSThierry Reding 
1158*dee8268fSThierry Reding 	platform_set_drvdata(pdev, dc);
1159*dee8268fSThierry Reding 
1160*dee8268fSThierry Reding 	return 0;
1161*dee8268fSThierry Reding }
1162*dee8268fSThierry Reding 
1163*dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev)
1164*dee8268fSThierry Reding {
1165*dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
1166*dee8268fSThierry Reding 	int err;
1167*dee8268fSThierry Reding 
1168*dee8268fSThierry Reding 	err = host1x_client_unregister(&dc->client);
1169*dee8268fSThierry Reding 	if (err < 0) {
1170*dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
1171*dee8268fSThierry Reding 			err);
1172*dee8268fSThierry Reding 		return err;
1173*dee8268fSThierry Reding 	}
1174*dee8268fSThierry Reding 
1175*dee8268fSThierry Reding 	clk_disable_unprepare(dc->clk);
1176*dee8268fSThierry Reding 
1177*dee8268fSThierry Reding 	return 0;
1178*dee8268fSThierry Reding }
1179*dee8268fSThierry Reding 
1180*dee8268fSThierry Reding static struct of_device_id tegra_dc_of_match[] = {
1181*dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra30-dc", },
1182*dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra20-dc", },
1183*dee8268fSThierry Reding 	{ },
1184*dee8268fSThierry Reding };
1185*dee8268fSThierry Reding 
1186*dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
1187*dee8268fSThierry Reding 	.driver = {
1188*dee8268fSThierry Reding 		.name = "tegra-dc",
1189*dee8268fSThierry Reding 		.owner = THIS_MODULE,
1190*dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
1191*dee8268fSThierry Reding 	},
1192*dee8268fSThierry Reding 	.probe = tegra_dc_probe,
1193*dee8268fSThierry Reding 	.remove = tegra_dc_remove,
1194*dee8268fSThierry Reding };
1195