xref: /freebsd-src/sys/arm/nvidia/drm2/tegra_dc.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
1a0a23564SMichal Meloun /*-
2a0a23564SMichal Meloun  * Copyright (c) 2015 Michal Meloun
3a0a23564SMichal Meloun  * All rights reserved.
4a0a23564SMichal Meloun  *
5a0a23564SMichal Meloun  * Redistribution and use in source and binary forms, with or without
6a0a23564SMichal Meloun  * modification, are permitted provided that the following conditions
7a0a23564SMichal Meloun  * are met:
8a0a23564SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9a0a23564SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10a0a23564SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11a0a23564SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12a0a23564SMichal Meloun  *    documentation and/or other materials provided with the distribution.
13a0a23564SMichal Meloun  *
14a0a23564SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a0a23564SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a0a23564SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a0a23564SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a0a23564SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a0a23564SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a0a23564SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a0a23564SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a0a23564SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a0a23564SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a0a23564SMichal Meloun  * SUCH DAMAGE.
25a0a23564SMichal Meloun  */
26a0a23564SMichal Meloun 
27a0a23564SMichal Meloun #include <sys/param.h>
28a0a23564SMichal Meloun #include <sys/systm.h>
29a0a23564SMichal Meloun #include <sys/bus.h>
30a0a23564SMichal Meloun #include <sys/gpio.h>
31a0a23564SMichal Meloun #include <sys/kernel.h>
32a0a23564SMichal Meloun #include <sys/module.h>
33a0a23564SMichal Meloun #include <sys/malloc.h>
34a0a23564SMichal Meloun #include <sys/rman.h>
35a0a23564SMichal Meloun #include <sys/sysctl.h>
36a0a23564SMichal Meloun 
37a0a23564SMichal Meloun #include <machine/bus.h>
38a0a23564SMichal Meloun 
39be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
401f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
41a0a23564SMichal Meloun #include <dev/drm2/drmP.h>
42a0a23564SMichal Meloun #include <dev/drm2/drm_crtc_helper.h>
43a0a23564SMichal Meloun #include <dev/drm2/drm_fb_helper.h>
44a0a23564SMichal Meloun #include <dev/drm2/drm_fixed.h>
45a0a23564SMichal Meloun #include <dev/ofw/ofw_bus.h>
46a0a23564SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
47a0a23564SMichal Meloun 
48a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_dc_reg.h>
49a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_drm.h>
50a0a23564SMichal Meloun #include <arm/nvidia/tegra_pmc.h>
51a0a23564SMichal Meloun 
52a0a23564SMichal Meloun #include "tegra_drm_if.h"
53a0a23564SMichal Meloun #include "tegra_dc_if.h"
54a0a23564SMichal Meloun 
55a0a23564SMichal Meloun #define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, 4 * (_r), (_v))
56a0a23564SMichal Meloun #define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, 4 * (_r))
57a0a23564SMichal Meloun 
58a0a23564SMichal Meloun #define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
59a0a23564SMichal Meloun #define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
60a0a23564SMichal Meloun #define	SLEEP(_sc, timeout)						\
61a0a23564SMichal Meloun 	mtx_sleep(sc, &sc->mtx, 0, "tegra_dc_wait", timeout);
62a0a23564SMichal Meloun #define	LOCK_INIT(_sc)							\
63a0a23564SMichal Meloun 	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_dc", MTX_DEF)
64a0a23564SMichal Meloun #define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
65a0a23564SMichal Meloun #define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
66a0a23564SMichal Meloun #define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
67a0a23564SMichal Meloun 
68a0a23564SMichal Meloun #define	SYNCPT_VBLANK0 26
69a0a23564SMichal Meloun #define	SYNCPT_VBLANK1 27
70a0a23564SMichal Meloun 
71a0a23564SMichal Meloun #define	DC_MAX_PLANES 2		/* Maximum planes */
72a0a23564SMichal Meloun 
73a0a23564SMichal Meloun /* DRM Formats supported by DC */
74a0a23564SMichal Meloun /* XXXX expand me */
75a0a23564SMichal Meloun static uint32_t dc_plane_formats[] = {
76a0a23564SMichal Meloun 	DRM_FORMAT_XBGR8888,
77a0a23564SMichal Meloun 	DRM_FORMAT_XRGB8888,
78a0a23564SMichal Meloun 	DRM_FORMAT_RGB565,
79a0a23564SMichal Meloun 	DRM_FORMAT_UYVY,
80a0a23564SMichal Meloun 	DRM_FORMAT_YUYV,
81a0a23564SMichal Meloun 	DRM_FORMAT_YUV420,
82a0a23564SMichal Meloun 	DRM_FORMAT_YUV422,
83a0a23564SMichal Meloun };
84a0a23564SMichal Meloun 
85a0a23564SMichal Meloun /* Complete description of one window (plane) */
86a0a23564SMichal Meloun struct dc_window {
87a0a23564SMichal Meloun 	/* Source (in framebuffer) rectangle, in pixels */
88a0a23564SMichal Meloun 	u_int			src_x;
89a0a23564SMichal Meloun 	u_int			src_y;
90a0a23564SMichal Meloun 	u_int			src_w;
91a0a23564SMichal Meloun 	u_int			src_h;
92a0a23564SMichal Meloun 
93a0a23564SMichal Meloun 	/* Destination (on display) rectangle, in pixels */
94a0a23564SMichal Meloun 	u_int			dst_x;
95a0a23564SMichal Meloun 	u_int			dst_y;
96a0a23564SMichal Meloun 	u_int			dst_w;
97a0a23564SMichal Meloun 	u_int			dst_h;
98a0a23564SMichal Meloun 
99a0a23564SMichal Meloun 	/* Parsed pixel format */
100a0a23564SMichal Meloun 	u_int			bits_per_pixel;
101a0a23564SMichal Meloun 	bool			is_yuv;		/* any YUV mode */
102a0a23564SMichal Meloun 	bool			is_yuv_planar;	/* planar YUV mode */
103a0a23564SMichal Meloun 	uint32_t 		color_mode;	/* DC_WIN_COLOR_DEPTH */
104a0a23564SMichal Meloun 	uint32_t		swap;		/* DC_WIN_BYTE_SWAP */
105a0a23564SMichal Meloun 	uint32_t		surface_kind;	/* DC_WINBUF_SURFACE_KIND */
106a0a23564SMichal Meloun 	uint32_t		block_height;	/* DC_WINBUF_SURFACE_KIND */
107a0a23564SMichal Meloun 
108a0a23564SMichal Meloun 	/* Parsed flipping, rotation is not supported for pitched modes */
109a0a23564SMichal Meloun 	bool			flip_x;		/* inverted X-axis */
110a0a23564SMichal Meloun 	bool			flip_y;		/* inverted Y-axis */
111a0a23564SMichal Meloun 	bool			transpose_xy;	/* swap X and Y-axis */
112a0a23564SMichal Meloun 
113a0a23564SMichal Meloun 	/* Color planes base addresses and strides */
114a0a23564SMichal Meloun 	bus_size_t		base[3];
115a0a23564SMichal Meloun 	uint32_t		stride[3];	/* stride[2] isn't used by HW */
116a0a23564SMichal Meloun };
117a0a23564SMichal Meloun 
118a0a23564SMichal Meloun struct dc_softc {
119a0a23564SMichal Meloun 	device_t		dev;
120a0a23564SMichal Meloun 	struct resource		*mem_res;
121a0a23564SMichal Meloun 	struct resource		*irq_res;
122a0a23564SMichal Meloun 	void			*irq_ih;
123a0a23564SMichal Meloun 	struct mtx		mtx;
124a0a23564SMichal Meloun 
125a0a23564SMichal Meloun 	clk_t			clk_parent;
126a0a23564SMichal Meloun 	clk_t			clk_dc;
127a0a23564SMichal Meloun 	hwreset_t		hwreset_dc;
128a0a23564SMichal Meloun 
129a0a23564SMichal Meloun 	int			pitch_align;
130a0a23564SMichal Meloun 
131a0a23564SMichal Meloun 	struct tegra_crtc 	tegra_crtc;
132a0a23564SMichal Meloun 	struct drm_pending_vblank_event *event;
133a0a23564SMichal Meloun 	struct drm_gem_object 	*cursor_gem;
134a0a23564SMichal Meloun };
135a0a23564SMichal Meloun 
136a0a23564SMichal Meloun static struct ofw_compat_data compat_data[] = {
137a0a23564SMichal Meloun 	{"nvidia,tegra124-dc",	1},
138a0a23564SMichal Meloun 	{NULL,			0},
139a0a23564SMichal Meloun };
140a0a23564SMichal Meloun 
141a0a23564SMichal Meloun /* Convert standard drm pixel format to tegra windows parameters. */
142a0a23564SMichal Meloun static int
143a0a23564SMichal Meloun dc_parse_drm_format(struct tegra_fb *fb, struct dc_window *win)
144a0a23564SMichal Meloun {
145a0a23564SMichal Meloun 	struct tegra_bo *bo;
146a0a23564SMichal Meloun 	uint32_t cm;
147a0a23564SMichal Meloun 	uint32_t sw;
148a0a23564SMichal Meloun 	bool is_yuv, is_yuv_planar;
149a0a23564SMichal Meloun 	int nplanes, i;
150a0a23564SMichal Meloun 
151a0a23564SMichal Meloun 	switch (fb->drm_fb.pixel_format) {
152a0a23564SMichal Meloun 	case DRM_FORMAT_XBGR8888:
153a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
154a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_R8G8B8A8;
155a0a23564SMichal Meloun 		is_yuv = false;
156a0a23564SMichal Meloun 		is_yuv_planar = false;
157a0a23564SMichal Meloun 		break;
158a0a23564SMichal Meloun 
159a0a23564SMichal Meloun 	case DRM_FORMAT_XRGB8888:
160a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
161a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_B8G8R8A8;
162a0a23564SMichal Meloun 		is_yuv = false;
163a0a23564SMichal Meloun 		is_yuv_planar = false;
164a0a23564SMichal Meloun 		break;
165a0a23564SMichal Meloun 
166a0a23564SMichal Meloun 	case DRM_FORMAT_RGB565:
167a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
168a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_B5G6R5;
169a0a23564SMichal Meloun 		is_yuv = false;
170a0a23564SMichal Meloun 		is_yuv_planar = false;
171a0a23564SMichal Meloun 		break;
172a0a23564SMichal Meloun 
173a0a23564SMichal Meloun 	case DRM_FORMAT_UYVY:
174a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
175a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_YCbCr422;
176a0a23564SMichal Meloun 		is_yuv = true;
177a0a23564SMichal Meloun 		is_yuv_planar = false;
178a0a23564SMichal Meloun 		break;
179a0a23564SMichal Meloun 
180a0a23564SMichal Meloun 	case DRM_FORMAT_YUYV:
181a0a23564SMichal Meloun 		sw = BYTE_SWAP(SWAP2);
182a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_YCbCr422;
183a0a23564SMichal Meloun 		is_yuv = true;
184a0a23564SMichal Meloun 		is_yuv_planar = false;
185a0a23564SMichal Meloun 		break;
186a0a23564SMichal Meloun 
187a0a23564SMichal Meloun 	case DRM_FORMAT_YUV420:
188a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
189a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_YCbCr420P;
190a0a23564SMichal Meloun 		is_yuv = true;
191a0a23564SMichal Meloun 		is_yuv_planar = true;
192a0a23564SMichal Meloun 		break;
193a0a23564SMichal Meloun 
194a0a23564SMichal Meloun 	case DRM_FORMAT_YUV422:
195a0a23564SMichal Meloun 		sw = BYTE_SWAP(NOSWAP);
196a0a23564SMichal Meloun 		cm = WIN_COLOR_DEPTH_YCbCr422P;
197a0a23564SMichal Meloun 		is_yuv = true;
198a0a23564SMichal Meloun 		is_yuv_planar = true;
199a0a23564SMichal Meloun 		break;
200a0a23564SMichal Meloun 
201a0a23564SMichal Meloun 	default:
202a0a23564SMichal Meloun 		/* Unsupported format */
203a0a23564SMichal Meloun 		return (-EINVAL);
204a0a23564SMichal Meloun 	}
205a0a23564SMichal Meloun 
206a0a23564SMichal Meloun 	/* Basic check of arguments. */
207a0a23564SMichal Meloun 	switch (fb->rotation) {
208a0a23564SMichal Meloun 	case 0:
209a0a23564SMichal Meloun 	case 180:
210a0a23564SMichal Meloun 		break;
211a0a23564SMichal Meloun 
212a0a23564SMichal Meloun 	case 90: 		/* Rotation is supported only */
213a0a23564SMichal Meloun 	case 270:		/*  for block linear surfaces */
214a0a23564SMichal Meloun 		if (!fb->block_linear)
215a0a23564SMichal Meloun 			return (-EINVAL);
216a0a23564SMichal Meloun 		break;
217a0a23564SMichal Meloun 
218a0a23564SMichal Meloun 	default:
219a0a23564SMichal Meloun 		return (-EINVAL);
220a0a23564SMichal Meloun 	}
221a0a23564SMichal Meloun 	/* XXX Add more checks (sizes, scaling...) */
222a0a23564SMichal Meloun 
223a0a23564SMichal Meloun 	if (win == NULL)
224a0a23564SMichal Meloun 		return (0);
225a0a23564SMichal Meloun 
226a0a23564SMichal Meloun 	win->surface_kind =
227a0a23564SMichal Meloun 	    fb->block_linear ? SURFACE_KIND_BL_16B2: SURFACE_KIND_PITCH;
228a0a23564SMichal Meloun 	win->block_height = fb->block_height;
229a0a23564SMichal Meloun 	switch (fb->rotation) {
230a0a23564SMichal Meloun 	case 0:					/* (0,0,0) */
231a0a23564SMichal Meloun 		win->transpose_xy = false;
232a0a23564SMichal Meloun 		win->flip_x = false;
233a0a23564SMichal Meloun 		win->flip_y = false;
234a0a23564SMichal Meloun 		break;
235a0a23564SMichal Meloun 
236a0a23564SMichal Meloun 	case 90:				/* (1,0,1) */
237a0a23564SMichal Meloun 		win->transpose_xy = true;
238a0a23564SMichal Meloun 		win->flip_x = false;
239a0a23564SMichal Meloun 		win->flip_y = true;
240a0a23564SMichal Meloun 		break;
241a0a23564SMichal Meloun 
242a0a23564SMichal Meloun 	case 180:				/* (0,1,1) */
243a0a23564SMichal Meloun 		win->transpose_xy = false;
244a0a23564SMichal Meloun 		win->flip_x = true;
245a0a23564SMichal Meloun 		win->flip_y = true;
246a0a23564SMichal Meloun 		break;
247a0a23564SMichal Meloun 
248a0a23564SMichal Meloun 	case 270:				/* (1,1,0) */
249a0a23564SMichal Meloun 		win->transpose_xy = true;
250a0a23564SMichal Meloun 		win->flip_x = true;
251a0a23564SMichal Meloun 		win->flip_y = false;
252a0a23564SMichal Meloun 		break;
253a0a23564SMichal Meloun 	}
254a0a23564SMichal Meloun 	win->flip_x ^= fb->flip_x;
255a0a23564SMichal Meloun 	win->flip_y ^= fb->flip_y;
256a0a23564SMichal Meloun 
257a0a23564SMichal Meloun 	win->color_mode = cm;
258a0a23564SMichal Meloun 	win->swap = sw;
259a0a23564SMichal Meloun 	win->bits_per_pixel = fb->drm_fb.bits_per_pixel;
260a0a23564SMichal Meloun 	win->is_yuv = is_yuv;
261a0a23564SMichal Meloun 	win->is_yuv_planar = is_yuv_planar;
262a0a23564SMichal Meloun 
263a0a23564SMichal Meloun 	nplanes = drm_format_num_planes(fb->drm_fb.pixel_format);
264a0a23564SMichal Meloun 	for (i = 0; i < nplanes; i++) {
265a0a23564SMichal Meloun 		bo = fb->planes[i];
266a0a23564SMichal Meloun 		win->base[i] = bo->pbase + fb->drm_fb.offsets[i];
267a0a23564SMichal Meloun 		win->stride[i] = fb->drm_fb.pitches[i];
268a0a23564SMichal Meloun 	}
269a0a23564SMichal Meloun 	return (0);
270a0a23564SMichal Meloun }
271a0a23564SMichal Meloun 
272a0a23564SMichal Meloun /*
273a0a23564SMichal Meloun  * Scaling functions.
274a0a23564SMichal Meloun  *
275a0a23564SMichal Meloun  * It's unclear if we want/must program the fractional portion
276a0a23564SMichal Meloun  * (aka bias) of init_dda registers, mainly when mirrored axis
277a0a23564SMichal Meloun  * modes are used.
278a0a23564SMichal Meloun  * For now, we use 1.0 as recommended by TRM.
279a0a23564SMichal Meloun  */
280a0a23564SMichal Meloun static inline uint32_t
281a0a23564SMichal Meloun dc_scaling_init(uint32_t start)
282a0a23564SMichal Meloun {
283a0a23564SMichal Meloun 
284a0a23564SMichal Meloun 	return (1 << 12);
285a0a23564SMichal Meloun }
286a0a23564SMichal Meloun 
287a0a23564SMichal Meloun static inline uint32_t
288a0a23564SMichal Meloun dc_scaling_incr(uint32_t src, uint32_t dst, uint32_t maxscale)
289a0a23564SMichal Meloun {
290a0a23564SMichal Meloun 	uint32_t val;
291a0a23564SMichal Meloun 
292a0a23564SMichal Meloun 	val = (src - 1) << 12 ; /* 4.12 fixed float */
293a0a23564SMichal Meloun 	val /= (dst - 1);
294a0a23564SMichal Meloun 	if (val  > (maxscale << 12))
295a0a23564SMichal Meloun 		val = maxscale << 12;
296a0a23564SMichal Meloun 	return val;
297a0a23564SMichal Meloun }
298a0a23564SMichal Meloun 
299a0a23564SMichal Meloun /* -------------------------------------------------------------------
300a0a23564SMichal Meloun  *
301a0a23564SMichal Meloun  *    HW Access.
302a0a23564SMichal Meloun  *
303a0a23564SMichal Meloun  */
304a0a23564SMichal Meloun 
305a0a23564SMichal Meloun /*
306a0a23564SMichal Meloun  * Setup pixel clock.
307a0a23564SMichal Meloun  * Minimal frequency is pixel clock, but output is free to select
308a0a23564SMichal Meloun  * any higher.
309a0a23564SMichal Meloun  */
310a0a23564SMichal Meloun static int
311a0a23564SMichal Meloun dc_setup_clk(struct dc_softc *sc, struct drm_crtc *crtc,
312a0a23564SMichal Meloun     struct drm_display_mode *mode, uint32_t *div)
313a0a23564SMichal Meloun {
314a0a23564SMichal Meloun 	uint64_t pclk, freq;
315a0a23564SMichal Meloun 	struct tegra_drm_encoder *output;
316a0a23564SMichal Meloun 	struct drm_encoder *encoder;
317a0a23564SMichal Meloun 	long rv;
318a0a23564SMichal Meloun 
319a0a23564SMichal Meloun 	pclk = mode->clock * 1000;
320a0a23564SMichal Meloun 
321a0a23564SMichal Meloun 	/* Find attached encoder */
322a0a23564SMichal Meloun 	output = NULL;
323a0a23564SMichal Meloun 	list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list,
324a0a23564SMichal Meloun 	    head) {
325a0a23564SMichal Meloun 		if (encoder->crtc == crtc) {
326a0a23564SMichal Meloun 			output = container_of(encoder, struct tegra_drm_encoder,
327a0a23564SMichal Meloun 			    encoder);
328a0a23564SMichal Meloun 			break;
329a0a23564SMichal Meloun 		}
330a0a23564SMichal Meloun 	}
331a0a23564SMichal Meloun 	if (output == NULL)
332a0a23564SMichal Meloun 		return (-ENODEV);
333a0a23564SMichal Meloun 
334a0a23564SMichal Meloun 	if (output->setup_clock == NULL)
335a0a23564SMichal Meloun 		panic("Output have not setup_clock function.\n");
336a0a23564SMichal Meloun 	rv = output->setup_clock(output, sc->clk_dc, pclk);
337a0a23564SMichal Meloun 	if (rv != 0) {
338a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot setup pixel clock: %llu\n",
339a0a23564SMichal Meloun 		    pclk);
340a0a23564SMichal Meloun 		return (rv);
341a0a23564SMichal Meloun 	}
342a0a23564SMichal Meloun 
343a0a23564SMichal Meloun 	rv = clk_get_freq(sc->clk_dc, &freq);
344a0a23564SMichal Meloun 	*div = (freq * 2 / pclk) - 2;
345a0a23564SMichal Meloun 
346a0a23564SMichal Meloun 	DRM_DEBUG_KMS("frequency: %llu, DC divider: %u\n", freq, *div);
347a0a23564SMichal Meloun 
348a0a23564SMichal Meloun 	return 0;
349a0a23564SMichal Meloun }
350a0a23564SMichal Meloun 
351a0a23564SMichal Meloun static void
352a0a23564SMichal Meloun dc_setup_window(struct dc_softc *sc, unsigned int index, struct dc_window *win)
353a0a23564SMichal Meloun {
354a0a23564SMichal Meloun 	uint32_t h_offset, v_offset, h_size, v_size, bpp;
355a0a23564SMichal Meloun 	uint32_t h_init_dda, v_init_dda, h_incr_dda, v_incr_dda;
356a0a23564SMichal Meloun 	uint32_t val;
357a0a23564SMichal Meloun 
358a0a23564SMichal Meloun #ifdef DMR_DEBUG_WINDOW
359a0a23564SMichal Meloun 	printf("%s window: %d\n", __func__, index);
360a0a23564SMichal Meloun 	printf("  src: x: %d, y: %d, w: %d, h: %d\n",
361a0a23564SMichal Meloun 	   win->src_x, win->src_y, win->src_w, win->src_h);
362a0a23564SMichal Meloun 	printf("  dst: x: %d, y: %d, w: %d, h: %d\n",
363a0a23564SMichal Meloun 	   win->dst_x, win->dst_y, win->dst_w, win->dst_h);
364a0a23564SMichal Meloun 	printf("  bpp: %d, color_mode: %d, swap: %d\n",
365a0a23564SMichal Meloun 	   win->bits_per_pixel, win->color_mode, win->swap);
366a0a23564SMichal Meloun #endif
367a0a23564SMichal Meloun 
368a0a23564SMichal Meloun 	if (win->is_yuv)
369a0a23564SMichal Meloun 		bpp = win->is_yuv_planar ? 1 : 2;
370a0a23564SMichal Meloun 	else
371a0a23564SMichal Meloun 		bpp = (win->bits_per_pixel + 7) / 8;
372a0a23564SMichal Meloun 
373a0a23564SMichal Meloun 	if (!win->transpose_xy) {
374a0a23564SMichal Meloun 		h_size = win->src_w * bpp;
375a0a23564SMichal Meloun 		v_size = win->src_h;
376a0a23564SMichal Meloun 	} else {
377a0a23564SMichal Meloun 		h_size = win->src_h * bpp;
378a0a23564SMichal Meloun 		v_size = win->src_w;
379a0a23564SMichal Meloun 	}
380a0a23564SMichal Meloun 
381aeb665b5SEd Maste 	h_offset = win->src_x * bpp;
382a0a23564SMichal Meloun 	v_offset = win->src_y;
383a0a23564SMichal Meloun 	if (win->flip_x) {
384a0a23564SMichal Meloun 		h_offset += win->src_w * bpp - 1;
385a0a23564SMichal Meloun 	}
386a0a23564SMichal Meloun 	if (win->flip_y)
387a0a23564SMichal Meloun 		v_offset += win->src_h - 1;
388a0a23564SMichal Meloun 
389a0a23564SMichal Meloun 	/* Adjust offsets for planar yuv modes */
390a0a23564SMichal Meloun 	if (win->is_yuv_planar) {
391a0a23564SMichal Meloun 		h_offset &= ~1;
392a0a23564SMichal Meloun 		if (win->flip_x )
393a0a23564SMichal Meloun 			h_offset |= 1;
394a0a23564SMichal Meloun 		v_offset &= ~1;
395a0a23564SMichal Meloun 		if (win->flip_y )
396a0a23564SMichal Meloun 			v_offset |= 1;
397a0a23564SMichal Meloun 	}
398a0a23564SMichal Meloun 
399a0a23564SMichal Meloun 	/* Setup scaling. */
400a0a23564SMichal Meloun 	if (!win->transpose_xy) {
401a0a23564SMichal Meloun 		h_init_dda = dc_scaling_init(win->src_x);
402a0a23564SMichal Meloun 		v_init_dda = dc_scaling_init(win->src_y);
403a0a23564SMichal Meloun 		h_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 4);
404a0a23564SMichal Meloun 		v_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 15);
405a0a23564SMichal Meloun 	} else {
406a0a23564SMichal Meloun 		h_init_dda =  dc_scaling_init(win->src_y);
407a0a23564SMichal Meloun 		v_init_dda =  dc_scaling_init(win->src_x);
408a0a23564SMichal Meloun 		h_incr_dda = dc_scaling_incr(win->src_h, win->dst_h, 4);
409a0a23564SMichal Meloun 		v_incr_dda = dc_scaling_incr(win->src_w, win->dst_w, 15);
410a0a23564SMichal Meloun 	}
411a0a23564SMichal Meloun #ifdef DMR_DEBUG_WINDOW
412a0a23564SMichal Meloun 	printf("\n");
413a0a23564SMichal Meloun 	printf("  bpp: %d, size: h: %d v: %d, offset: h:%d v: %d\n",
414a0a23564SMichal Meloun 	   bpp, h_size, v_size, h_offset, v_offset);
415a0a23564SMichal Meloun 	printf("  init_dda: h: %d v: %d, incr_dda: h: %d v: %d\n",
416a0a23564SMichal Meloun 	   h_init_dda, v_init_dda, h_incr_dda, v_incr_dda);
417a0a23564SMichal Meloun #endif
418a0a23564SMichal Meloun 
419a0a23564SMichal Meloun 	LOCK(sc);
420a0a23564SMichal Meloun 
421a0a23564SMichal Meloun 	/* Select target window  */
422a0a23564SMichal Meloun 	val = WINDOW_A_SELECT << index;
423a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, val);
424a0a23564SMichal Meloun 
425a0a23564SMichal Meloun 	/* Sizes */
426a0a23564SMichal Meloun 	WR4(sc, DC_WIN_POSITION, WIN_POSITION(win->dst_x, win->dst_y));
427a0a23564SMichal Meloun 	WR4(sc, DC_WIN_SIZE, WIN_SIZE(win->dst_w, win->dst_h));
428a0a23564SMichal Meloun 	WR4(sc, DC_WIN_PRESCALED_SIZE, WIN_PRESCALED_SIZE(h_size, v_size));
429a0a23564SMichal Meloun 
430a0a23564SMichal Meloun 	/* DDA */
431a0a23564SMichal Meloun 	WR4(sc, DC_WIN_DDA_INCREMENT,
432a0a23564SMichal Meloun 	    WIN_DDA_INCREMENT(h_incr_dda, v_incr_dda));
433a0a23564SMichal Meloun 	WR4(sc, DC_WIN_H_INITIAL_DDA, h_init_dda);
434a0a23564SMichal Meloun 	WR4(sc, DC_WIN_V_INITIAL_DDA, v_init_dda);
435a0a23564SMichal Meloun 
436a0a23564SMichal Meloun 	/* Color planes base addresses and strides */
437a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_START_ADDR, win->base[0]);
438a0a23564SMichal Meloun 	if (win->is_yuv_planar) {
439a0a23564SMichal Meloun 		WR4(sc, DC_WINBUF_START_ADDR_U, win->base[1]);
440a0a23564SMichal Meloun 		WR4(sc, DC_WINBUF_START_ADDR_V, win->base[2]);
441a0a23564SMichal Meloun 		WR4(sc, DC_WIN_LINE_STRIDE,
442a0a23564SMichal Meloun 		     win->stride[1] << 16 | win->stride[0]);
443a0a23564SMichal Meloun 	} else {
444a0a23564SMichal Meloun 		WR4(sc, DC_WIN_LINE_STRIDE, win->stride[0]);
445a0a23564SMichal Meloun 	}
446a0a23564SMichal Meloun 
447a0a23564SMichal Meloun 	/* Offsets for rotation and axis flip */
448a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_ADDR_H_OFFSET, h_offset);
449a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_ADDR_V_OFFSET, v_offset);
450a0a23564SMichal Meloun 
451a0a23564SMichal Meloun 	/* Color format */
452a0a23564SMichal Meloun 	WR4(sc, DC_WIN_COLOR_DEPTH, win->color_mode);
453a0a23564SMichal Meloun 	WR4(sc, DC_WIN_BYTE_SWAP, win->swap);
454a0a23564SMichal Meloun 
455a0a23564SMichal Meloun 	/* Tiling */
456a0a23564SMichal Meloun 	val = win->surface_kind;
457a0a23564SMichal Meloun 	if (win->surface_kind == SURFACE_KIND_BL_16B2)
458a0a23564SMichal Meloun 		val |= SURFACE_KIND_BLOCK_HEIGHT(win->block_height);
459a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_SURFACE_KIND, val);
460a0a23564SMichal Meloun 
461a0a23564SMichal Meloun 	/* Color space coefs for YUV modes */
462a0a23564SMichal Meloun 	if (win->is_yuv) {
463a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_YOF,   0x00f0);
464a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KYRGB, 0x012a);
465a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KUR,   0x0000);
466a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KVR,   0x0198);
467a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KUG,   0x039b);
468a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KVG,   0x032f);
469a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KUB,   0x0204);
470a0a23564SMichal Meloun 		WR4(sc, DC_WINC_CSC_KVB,   0x0000);
471a0a23564SMichal Meloun 	}
472a0a23564SMichal Meloun 
473a0a23564SMichal Meloun 	val = WIN_ENABLE;
474a0a23564SMichal Meloun 	if (win->is_yuv)
475a0a23564SMichal Meloun 		val |= CSC_ENABLE;
476a0a23564SMichal Meloun 	else if (win->bits_per_pixel < 24)
477a0a23564SMichal Meloun 		val |= COLOR_EXPAND;
478a0a23564SMichal Meloun 	if (win->flip_y)
479a0a23564SMichal Meloun 		val |= V_DIRECTION;
480a0a23564SMichal Meloun 	if (win->flip_x)
481a0a23564SMichal Meloun 		val |= H_DIRECTION;
482a0a23564SMichal Meloun 	if (win->transpose_xy)
483a0a23564SMichal Meloun 		val |= SCAN_COLUMN;
484a0a23564SMichal Meloun 	WR4(sc, DC_WINC_WIN_OPTIONS, val);
485a0a23564SMichal Meloun 
486a0a23564SMichal Meloun #ifdef DMR_DEBUG_WINDOW
487a0a23564SMichal Meloun 	/* Set underflow debug mode -> highlight missing pixels. */
488a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_UFLOW_CTRL, UFLOW_CTR_ENABLE);
489a0a23564SMichal Meloun 	WR4(sc, DC_WINBUF_UFLOW_DBG_PIXEL, 0xFFFF0000);
490a0a23564SMichal Meloun #endif
491a0a23564SMichal Meloun 
492a0a23564SMichal Meloun 	UNLOCK(sc);
493a0a23564SMichal Meloun }
494a0a23564SMichal Meloun 
495a0a23564SMichal Meloun /* -------------------------------------------------------------------
496a0a23564SMichal Meloun  *
497a0a23564SMichal Meloun  *    Plane functions.
498a0a23564SMichal Meloun  *
499a0a23564SMichal Meloun  */
500a0a23564SMichal Meloun static int
501a0a23564SMichal Meloun dc_plane_update(struct drm_plane *drm_plane, struct drm_crtc *drm_crtc,
502a0a23564SMichal Meloun     struct drm_framebuffer *drm_fb,
503a0a23564SMichal Meloun     int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h,
504a0a23564SMichal Meloun     uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
505a0a23564SMichal Meloun {
506a0a23564SMichal Meloun 	struct tegra_plane *plane;
507a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
508a0a23564SMichal Meloun 	struct tegra_fb *fb;
509a0a23564SMichal Meloun 	struct dc_softc *sc;
510a0a23564SMichal Meloun 	struct dc_window win;
511a0a23564SMichal Meloun 	int rv;
512a0a23564SMichal Meloun 
513a0a23564SMichal Meloun 	plane = container_of(drm_plane, struct tegra_plane, drm_plane);
514a0a23564SMichal Meloun 	fb = container_of(drm_fb, struct tegra_fb, drm_fb);
515a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
516a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
517a0a23564SMichal Meloun 
518a0a23564SMichal Meloun 	memset(&win, 0, sizeof(win));
519a0a23564SMichal Meloun 	win.src_x = src_x >> 16;
520a0a23564SMichal Meloun 	win.src_y = src_y >> 16;
521a0a23564SMichal Meloun 	win.src_w = src_w >> 16;
522a0a23564SMichal Meloun 	win.src_h = src_h >> 16;
523a0a23564SMichal Meloun 	win.dst_x = crtc_x;
524a0a23564SMichal Meloun 	win.dst_y = crtc_y;
525a0a23564SMichal Meloun 	win.dst_w = crtc_w;
526a0a23564SMichal Meloun 	win.dst_h = crtc_h;
527a0a23564SMichal Meloun 
528a0a23564SMichal Meloun 	rv = dc_parse_drm_format(fb, &win);
529a0a23564SMichal Meloun 	if (rv != 0) {
530a0a23564SMichal Meloun 		DRM_WARNING("unsupported pixel format %d\n",
531a0a23564SMichal Meloun 		    fb->drm_fb.pixel_format);
532a0a23564SMichal Meloun 		return (rv);
533a0a23564SMichal Meloun 	}
534a0a23564SMichal Meloun 
535a0a23564SMichal Meloun 	dc_setup_window(sc, plane->index, &win);
536a0a23564SMichal Meloun 
537a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << plane->index);
538a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << plane->index);
539a0a23564SMichal Meloun 
540a0a23564SMichal Meloun 	return (0);
541a0a23564SMichal Meloun }
542a0a23564SMichal Meloun 
543a0a23564SMichal Meloun static int
544a0a23564SMichal Meloun dc_plane_disable(struct drm_plane *drm_plane)
545a0a23564SMichal Meloun {
546a0a23564SMichal Meloun 	struct tegra_plane *plane;
547a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
548a0a23564SMichal Meloun 	struct dc_softc *sc;
549a0a23564SMichal Meloun 	uint32_t val, idx;
550a0a23564SMichal Meloun 
551a0a23564SMichal Meloun 	if (drm_plane->crtc == NULL)
552a0a23564SMichal Meloun 		return (0);
553a0a23564SMichal Meloun 	plane = container_of(drm_plane, struct tegra_plane, drm_plane);
554a0a23564SMichal Meloun 	crtc = container_of(drm_plane->crtc, struct tegra_crtc, drm_crtc);
555a0a23564SMichal Meloun 
556a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
557a0a23564SMichal Meloun 	idx = plane->index;
558a0a23564SMichal Meloun 
559a0a23564SMichal Meloun 	LOCK(sc);
560a0a23564SMichal Meloun 
561a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT << idx);
562a0a23564SMichal Meloun 
563a0a23564SMichal Meloun 	val = RD4(sc, DC_WINC_WIN_OPTIONS);
564a0a23564SMichal Meloun 	val &= ~WIN_ENABLE;
565a0a23564SMichal Meloun 	WR4(sc, DC_WINC_WIN_OPTIONS, val);
566a0a23564SMichal Meloun 
567a0a23564SMichal Meloun 	UNLOCK(sc);
568a0a23564SMichal Meloun 
569a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_UPDATE << idx);
570a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, WIN_A_ACT_REQ << idx);
571a0a23564SMichal Meloun 
572a0a23564SMichal Meloun 	return (0);
573a0a23564SMichal Meloun }
574a0a23564SMichal Meloun 
575a0a23564SMichal Meloun static void
576a0a23564SMichal Meloun dc_plane_destroy(struct drm_plane *plane)
577a0a23564SMichal Meloun {
578a0a23564SMichal Meloun 
579a0a23564SMichal Meloun 	dc_plane_disable(plane);
580a0a23564SMichal Meloun 	drm_plane_cleanup(plane);
581a0a23564SMichal Meloun 	free(plane, DRM_MEM_KMS);
582a0a23564SMichal Meloun }
583a0a23564SMichal Meloun 
584a0a23564SMichal Meloun static const struct drm_plane_funcs dc_plane_funcs = {
585a0a23564SMichal Meloun 	.update_plane = dc_plane_update,
586a0a23564SMichal Meloun 	.disable_plane = dc_plane_disable,
587a0a23564SMichal Meloun 	.destroy = dc_plane_destroy,
588a0a23564SMichal Meloun };
589a0a23564SMichal Meloun 
590a0a23564SMichal Meloun /* -------------------------------------------------------------------
591a0a23564SMichal Meloun  *
592a0a23564SMichal Meloun  *    CRTC helper functions.
593a0a23564SMichal Meloun  *
594a0a23564SMichal Meloun  */
595a0a23564SMichal Meloun static void
596a0a23564SMichal Meloun dc_crtc_dpms(struct drm_crtc *crtc, int mode)
597a0a23564SMichal Meloun {
598a0a23564SMichal Meloun 	/* Empty function */
599a0a23564SMichal Meloun }
600a0a23564SMichal Meloun 
601a0a23564SMichal Meloun static bool
602a0a23564SMichal Meloun dc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
603a0a23564SMichal Meloun     struct drm_display_mode *adjusted)
604a0a23564SMichal Meloun {
605a0a23564SMichal Meloun 
606a0a23564SMichal Meloun 	return (true);
607a0a23564SMichal Meloun }
608a0a23564SMichal Meloun 
609a0a23564SMichal Meloun static int
610a0a23564SMichal Meloun dc_set_base(struct dc_softc *sc, int x, int y, struct tegra_fb *fb)
611a0a23564SMichal Meloun {
612a0a23564SMichal Meloun 	struct dc_window win;
613a0a23564SMichal Meloun 	int rv;
614a0a23564SMichal Meloun 
615a0a23564SMichal Meloun 	memset(&win, 0, sizeof(win));
616a0a23564SMichal Meloun 	win.src_x = x;
617a0a23564SMichal Meloun 	win.src_y = y;
618a0a23564SMichal Meloun 	win.src_w = fb->drm_fb.width;
619a0a23564SMichal Meloun 	win.src_h = fb->drm_fb.height;
620a0a23564SMichal Meloun 	win.dst_x = x;
621a0a23564SMichal Meloun 	win.dst_y = y;
622a0a23564SMichal Meloun 	win.dst_w = fb->drm_fb.width;
623a0a23564SMichal Meloun 	win.dst_h = fb->drm_fb.height;
624a0a23564SMichal Meloun 
625a0a23564SMichal Meloun 	rv = dc_parse_drm_format(fb, &win);
626a0a23564SMichal Meloun 	if (rv != 0) {
627a0a23564SMichal Meloun 		DRM_WARNING("unsupported pixel format %d\n",
628a0a23564SMichal Meloun 		    fb->drm_fb.pixel_format);
629a0a23564SMichal Meloun 		return (rv);
630a0a23564SMichal Meloun 	}
631a0a23564SMichal Meloun 	dc_setup_window(sc, 0, &win);
632a0a23564SMichal Meloun 
633a0a23564SMichal Meloun 	return (0);
634a0a23564SMichal Meloun }
635a0a23564SMichal Meloun 
636a0a23564SMichal Meloun static int
637a0a23564SMichal Meloun dc_crtc_mode_set(struct drm_crtc *drm_crtc, struct drm_display_mode *mode,
638a0a23564SMichal Meloun     struct drm_display_mode *adjusted, int x, int y,
639a0a23564SMichal Meloun     struct drm_framebuffer *old_fb)
640a0a23564SMichal Meloun {
641a0a23564SMichal Meloun 	struct dc_softc *sc;
642a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
643a0a23564SMichal Meloun 	struct tegra_fb *fb;
644a0a23564SMichal Meloun 	struct dc_window win;
645a0a23564SMichal Meloun 	uint32_t div, h_ref_to_sync, v_ref_to_sync;
646a0a23564SMichal Meloun 	int rv;
647a0a23564SMichal Meloun 
648a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
649a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
650a0a23564SMichal Meloun 	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
651a0a23564SMichal Meloun 
652a0a23564SMichal Meloun 	h_ref_to_sync = 1;
653a0a23564SMichal Meloun 	v_ref_to_sync = 1;
654a0a23564SMichal Meloun 	/* Setup timing */
655a0a23564SMichal Meloun 	rv = dc_setup_clk(sc, drm_crtc, mode, &div);
656a0a23564SMichal Meloun 	if (rv != 0) {
657a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot set pixel clock\n");
658a0a23564SMichal Meloun 		return (rv);
659a0a23564SMichal Meloun 	}
660a0a23564SMichal Meloun 
661a0a23564SMichal Meloun 	/* Timing */
662a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, 0);
663a0a23564SMichal Meloun 
664a0a23564SMichal Meloun 	WR4(sc, DC_DISP_REF_TO_SYNC,
665a0a23564SMichal Meloun 	    (v_ref_to_sync << 16) |
666a0a23564SMichal Meloun 	     h_ref_to_sync);
667a0a23564SMichal Meloun 
668a0a23564SMichal Meloun 	WR4(sc, DC_DISP_SYNC_WIDTH,
669a0a23564SMichal Meloun 	    ((mode->vsync_end - mode->vsync_start) << 16) |
670a0a23564SMichal Meloun 	    ((mode->hsync_end - mode->hsync_start) <<  0));
671a0a23564SMichal Meloun 
672a0a23564SMichal Meloun 	WR4(sc, DC_DISP_BACK_PORCH,
673a0a23564SMichal Meloun 	    ((mode->vtotal - mode->vsync_end) << 16) |
674a0a23564SMichal Meloun 	    ((mode->htotal - mode->hsync_end) <<  0));
675a0a23564SMichal Meloun 
676a0a23564SMichal Meloun 	WR4(sc, DC_DISP_FRONT_PORCH,
677a0a23564SMichal Meloun 	    ((mode->vsync_start - mode->vdisplay) << 16) |
678a0a23564SMichal Meloun 	    ((mode->hsync_start - mode->hdisplay) <<  0));
679a0a23564SMichal Meloun 
680a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_ACTIVE,
681a0a23564SMichal Meloun 	    (mode->vdisplay << 16) | mode->hdisplay);
682a0a23564SMichal Meloun 
683a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT(DF1P1C));
684a0a23564SMichal Meloun 
685a0a23564SMichal Meloun 	WR4(sc,DC_DISP_DISP_CLOCK_CONTROL,
686a0a23564SMichal Meloun 	    SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER(PCD1));
687a0a23564SMichal Meloun 
688a0a23564SMichal Meloun 	memset(&win, 0, sizeof(win));
689a0a23564SMichal Meloun 	win.src_x = x;
690a0a23564SMichal Meloun 	win.src_y = y;
691a0a23564SMichal Meloun 	win.src_w = mode->hdisplay;
692a0a23564SMichal Meloun 	win.src_h = mode->vdisplay;
693a0a23564SMichal Meloun 	win.dst_x = x;
694a0a23564SMichal Meloun 	win.dst_y = y;
695a0a23564SMichal Meloun 	win.dst_w = mode->hdisplay;
696a0a23564SMichal Meloun 	win.dst_h = mode->vdisplay;
697a0a23564SMichal Meloun 
698a0a23564SMichal Meloun 	rv = dc_parse_drm_format(fb, &win);
699a0a23564SMichal Meloun 	if (rv != 0) {
700a0a23564SMichal Meloun 		DRM_WARNING("unsupported pixel format %d\n",
701a0a23564SMichal Meloun 		    drm_crtc->fb->pixel_format);
702a0a23564SMichal Meloun 		return (rv);
703a0a23564SMichal Meloun 	}
704a0a23564SMichal Meloun 
705a0a23564SMichal Meloun 	dc_setup_window(sc, 0, &win);
706a0a23564SMichal Meloun 
707a0a23564SMichal Meloun 	return (0);
708a0a23564SMichal Meloun 
709a0a23564SMichal Meloun }
710a0a23564SMichal Meloun 
711a0a23564SMichal Meloun static int
712a0a23564SMichal Meloun dc_crtc_mode_set_base(struct drm_crtc *drm_crtc, int x, int y,
713a0a23564SMichal Meloun     struct drm_framebuffer *old_fb)
714a0a23564SMichal Meloun {
715a0a23564SMichal Meloun 	struct dc_softc *sc;
716a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
717a0a23564SMichal Meloun 	struct tegra_fb *fb;
718a0a23564SMichal Meloun 	int rv;
719a0a23564SMichal Meloun 
720a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
721a0a23564SMichal Meloun 	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
722a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
723a0a23564SMichal Meloun 
724a0a23564SMichal Meloun 	rv = dc_set_base(sc, x, y, fb);
725a0a23564SMichal Meloun 
726a0a23564SMichal Meloun 	/* Commit */
727a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
728a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ);
729a0a23564SMichal Meloun 	return (rv);
730a0a23564SMichal Meloun }
731a0a23564SMichal Meloun 
732a0a23564SMichal Meloun static void
733a0a23564SMichal Meloun dc_crtc_prepare(struct drm_crtc *drm_crtc)
734a0a23564SMichal Meloun {
735a0a23564SMichal Meloun 
736a0a23564SMichal Meloun 	struct dc_softc *sc;
737a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
738a0a23564SMichal Meloun 	uint32_t val;
739a0a23564SMichal Meloun 
740a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
741a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
742a0a23564SMichal Meloun 
743a0a23564SMichal Meloun 	WR4(sc, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL);
744a0a23564SMichal Meloun 	/* XXX allocate syncpoint from host1x */
745a0a23564SMichal Meloun 	WR4(sc, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE |
746a0a23564SMichal Meloun 	    (sc->tegra_crtc.nvidia_head == 0 ? SYNCPT_VBLANK0: SYNCPT_VBLANK1));
747a0a23564SMichal Meloun 
748a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_POWER_CONTROL,
749a0a23564SMichal Meloun 	    PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
750a0a23564SMichal Meloun 	    PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
751a0a23564SMichal Meloun 
752a0a23564SMichal Meloun 	val = RD4(sc, DC_CMD_DISPLAY_COMMAND);
753a0a23564SMichal Meloun 	val |= DISPLAY_CTRL_MODE(CTRL_MODE_C_DISPLAY);
754a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_COMMAND, val);
755a0a23564SMichal Meloun 
756a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_MASK,
757a0a23564SMichal Meloun 	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
758a0a23564SMichal Meloun 	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
759a0a23564SMichal Meloun 
760a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_ENABLE,
761a0a23564SMichal Meloun 	    VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
762a0a23564SMichal Meloun 	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
763a0a23564SMichal Meloun }
764a0a23564SMichal Meloun 
765a0a23564SMichal Meloun static void
766a0a23564SMichal Meloun dc_crtc_commit(struct drm_crtc *drm_crtc)
767a0a23564SMichal Meloun {
768a0a23564SMichal Meloun 	struct dc_softc *sc;
769a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
770a0a23564SMichal Meloun 	uint32_t val;
771a0a23564SMichal Meloun 
772a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
773a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
774a0a23564SMichal Meloun 
775a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
776a0a23564SMichal Meloun 
777a0a23564SMichal Meloun 	val = RD4(sc, DC_CMD_INT_MASK);
778a0a23564SMichal Meloun 	val |= FRAME_END_INT;
779a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_MASK, val);
780a0a23564SMichal Meloun 
781a0a23564SMichal Meloun 	val = RD4(sc, DC_CMD_INT_ENABLE);
782a0a23564SMichal Meloun 	val |= FRAME_END_INT;
783a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_ENABLE, val);
784a0a23564SMichal Meloun 
785a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL,  GENERAL_ACT_REQ | WIN_A_ACT_REQ);
786a0a23564SMichal Meloun }
787a0a23564SMichal Meloun 
788a0a23564SMichal Meloun static void
789a0a23564SMichal Meloun dc_crtc_load_lut(struct drm_crtc *crtc)
790a0a23564SMichal Meloun {
791a0a23564SMichal Meloun 
792a0a23564SMichal Meloun 	/* empty function */
793a0a23564SMichal Meloun }
794a0a23564SMichal Meloun 
795a0a23564SMichal Meloun static const struct drm_crtc_helper_funcs dc_crtc_helper_funcs = {
796a0a23564SMichal Meloun 	.dpms = dc_crtc_dpms,
797a0a23564SMichal Meloun 	.mode_fixup = dc_crtc_mode_fixup,
798a0a23564SMichal Meloun 	.mode_set = dc_crtc_mode_set,
799a0a23564SMichal Meloun 	.mode_set_base = dc_crtc_mode_set_base,
800a0a23564SMichal Meloun 	.prepare = dc_crtc_prepare,
801a0a23564SMichal Meloun 	.commit = dc_crtc_commit,
802a0a23564SMichal Meloun 	.load_lut = dc_crtc_load_lut,
803a0a23564SMichal Meloun };
804a0a23564SMichal Meloun 
805a0a23564SMichal Meloun static int
806a0a23564SMichal Meloun drm_crtc_index(struct drm_crtc *crtc)
807a0a23564SMichal Meloun {
808a0a23564SMichal Meloun 	int idx;
809a0a23564SMichal Meloun 	struct drm_crtc *tmp;
810a0a23564SMichal Meloun 
811a0a23564SMichal Meloun 	idx = 0;
812a0a23564SMichal Meloun 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
813a0a23564SMichal Meloun 		if (tmp == crtc)
814a0a23564SMichal Meloun 			return (idx);
815a0a23564SMichal Meloun 		idx++;
816a0a23564SMichal Meloun 	}
817a0a23564SMichal Meloun 	panic("Cannot find CRTC");
818a0a23564SMichal Meloun }
819a0a23564SMichal Meloun 
820a0a23564SMichal Meloun /* -------------------------------------------------------------------
821a0a23564SMichal Meloun  *
822a0a23564SMichal Meloun  *   Exported functions (mainly vsync related).
823a0a23564SMichal Meloun  *
824a0a23564SMichal Meloun  * XXX revisit this -> convert to bus methods?
825a0a23564SMichal Meloun  */
826a0a23564SMichal Meloun int
827a0a23564SMichal Meloun tegra_dc_get_pipe(struct drm_crtc *drm_crtc)
828a0a23564SMichal Meloun {
829a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
830a0a23564SMichal Meloun 
831a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
832a0a23564SMichal Meloun 	return (crtc->nvidia_head);
833a0a23564SMichal Meloun }
834a0a23564SMichal Meloun 
835a0a23564SMichal Meloun void
836a0a23564SMichal Meloun tegra_dc_enable_vblank(struct drm_crtc *drm_crtc)
837a0a23564SMichal Meloun {
838a0a23564SMichal Meloun 	struct dc_softc *sc;
839a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
840a0a23564SMichal Meloun 	uint32_t val;
841a0a23564SMichal Meloun 
842a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
843a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
844a0a23564SMichal Meloun 
845a0a23564SMichal Meloun 	LOCK(sc);
846a0a23564SMichal Meloun 	val = RD4(sc, DC_CMD_INT_MASK);
847a0a23564SMichal Meloun 	val |= VBLANK_INT;
848a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_MASK, val);
849a0a23564SMichal Meloun 	UNLOCK(sc);
850a0a23564SMichal Meloun }
851a0a23564SMichal Meloun 
852a0a23564SMichal Meloun void
853a0a23564SMichal Meloun tegra_dc_disable_vblank(struct drm_crtc *drm_crtc)
854a0a23564SMichal Meloun {
855a0a23564SMichal Meloun 	struct dc_softc *sc;
856a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
857a0a23564SMichal Meloun 	uint32_t val;
858a0a23564SMichal Meloun 
859a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
860a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
861a0a23564SMichal Meloun 
862a0a23564SMichal Meloun 	LOCK(sc);
863a0a23564SMichal Meloun 	val = RD4(sc, DC_CMD_INT_MASK);
864a0a23564SMichal Meloun 	val &= ~VBLANK_INT;
865a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_MASK, val);
866a0a23564SMichal Meloun 	UNLOCK(sc);
867a0a23564SMichal Meloun }
868a0a23564SMichal Meloun 
869a0a23564SMichal Meloun static void
870a0a23564SMichal Meloun dc_finish_page_flip(struct dc_softc *sc)
871a0a23564SMichal Meloun {
872a0a23564SMichal Meloun 	struct drm_crtc *drm_crtc;
873a0a23564SMichal Meloun 	struct drm_device *drm;
874a0a23564SMichal Meloun 	struct tegra_fb *fb;
875a0a23564SMichal Meloun 	struct tegra_bo *bo;
876a0a23564SMichal Meloun 	uint32_t base;
877a0a23564SMichal Meloun 	int idx;
878a0a23564SMichal Meloun 
879a0a23564SMichal Meloun 	drm_crtc = &sc->tegra_crtc.drm_crtc;
880a0a23564SMichal Meloun 	drm = drm_crtc->dev;
881a0a23564SMichal Meloun 	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
882a0a23564SMichal Meloun 
883a0a23564SMichal Meloun 	mtx_lock(&drm->event_lock);
884a0a23564SMichal Meloun 
885a0a23564SMichal Meloun 	if (sc->event == NULL) {
886a0a23564SMichal Meloun 		mtx_unlock(&drm->event_lock);
887a0a23564SMichal Meloun 		return;
888a0a23564SMichal Meloun 	}
889a0a23564SMichal Meloun 
890a0a23564SMichal Meloun 	LOCK(sc);
891a0a23564SMichal Meloun 	/* Read active copy of WINBUF_START_ADDR */
892a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT);
893a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_ACCESS, READ_MUX);
894a0a23564SMichal Meloun 	base = RD4(sc, DC_WINBUF_START_ADDR);
895a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_ACCESS, 0);
896a0a23564SMichal Meloun 	UNLOCK(sc);
897a0a23564SMichal Meloun 
898a0a23564SMichal Meloun 	/* Is already active */
899a0a23564SMichal Meloun 	bo = tegra_fb_get_plane(fb, 0);
900a0a23564SMichal Meloun 	if (base == (bo->pbase + fb->drm_fb.offsets[0])) {
901a0a23564SMichal Meloun 		idx = drm_crtc_index(drm_crtc);
902a0a23564SMichal Meloun 		drm_send_vblank_event(drm, idx, sc->event);
903a0a23564SMichal Meloun 		drm_vblank_put(drm, idx);
904a0a23564SMichal Meloun 		sc->event = NULL;
905a0a23564SMichal Meloun 	}
906a0a23564SMichal Meloun 
907a0a23564SMichal Meloun 	mtx_unlock(&drm->event_lock);
908a0a23564SMichal Meloun }
909a0a23564SMichal Meloun 
910a0a23564SMichal Meloun void
911a0a23564SMichal Meloun tegra_dc_cancel_page_flip(struct drm_crtc *drm_crtc, struct drm_file *file)
912a0a23564SMichal Meloun {
913a0a23564SMichal Meloun 	struct dc_softc *sc;
914a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
915a0a23564SMichal Meloun 	struct drm_device *drm;
916a0a23564SMichal Meloun 
917a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
918a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
919a0a23564SMichal Meloun 	drm = drm_crtc->dev;
920a0a23564SMichal Meloun 	mtx_lock(&drm->event_lock);
921a0a23564SMichal Meloun 
922a0a23564SMichal Meloun 	if ((sc->event != NULL) && (sc->event->base.file_priv == file)) {
923a0a23564SMichal Meloun 		sc->event->base.destroy(&sc->event->base);
924a0a23564SMichal Meloun 		drm_vblank_put(drm, drm_crtc_index(drm_crtc));
925a0a23564SMichal Meloun 		sc->event = NULL;
926a0a23564SMichal Meloun 	}
927a0a23564SMichal Meloun 	mtx_unlock(&drm->event_lock);
928a0a23564SMichal Meloun }
929a0a23564SMichal Meloun 
930a0a23564SMichal Meloun /* -------------------------------------------------------------------
931a0a23564SMichal Meloun  *
932a0a23564SMichal Meloun  *    CRTC functions.
933a0a23564SMichal Meloun  *
934a0a23564SMichal Meloun  */
935a0a23564SMichal Meloun static int
936a0a23564SMichal Meloun dc_page_flip(struct drm_crtc *drm_crtc, struct drm_framebuffer *drm_fb,
937a0a23564SMichal Meloun     struct drm_pending_vblank_event *event)
938a0a23564SMichal Meloun {
939a0a23564SMichal Meloun 	struct dc_softc *sc;
940a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
941a0a23564SMichal Meloun 	struct tegra_fb *fb;
942a0a23564SMichal Meloun 	struct drm_device *drm;
943a0a23564SMichal Meloun 
944a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
945a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
946a0a23564SMichal Meloun 	fb = container_of(drm_crtc->fb, struct tegra_fb, drm_fb);
947a0a23564SMichal Meloun 	drm = drm_crtc->dev;
948a0a23564SMichal Meloun 
949a0a23564SMichal Meloun 	if (sc->event != NULL)
950a0a23564SMichal Meloun 		return (-EBUSY);
951a0a23564SMichal Meloun 
952a0a23564SMichal Meloun 	if (event != NULL) {
953a0a23564SMichal Meloun 		event->pipe = sc->tegra_crtc.nvidia_head;
954a0a23564SMichal Meloun 		sc->event = event;
955a0a23564SMichal Meloun 		drm_vblank_get(drm, event->pipe);
956a0a23564SMichal Meloun 	}
957a0a23564SMichal Meloun 
958a0a23564SMichal Meloun 	dc_set_base(sc, drm_crtc->x, drm_crtc->y, fb);
959a0a23564SMichal Meloun 	drm_crtc->fb = drm_fb;
960a0a23564SMichal Meloun 
961a0a23564SMichal Meloun 	/* Commit */
962a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE);
963a0a23564SMichal Meloun 
964a0a23564SMichal Meloun 	return (0);
965a0a23564SMichal Meloun }
966a0a23564SMichal Meloun 
967a0a23564SMichal Meloun static int
968a0a23564SMichal Meloun dc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file,
969a0a23564SMichal Meloun     uint32_t handle, uint32_t width, uint32_t height)
970a0a23564SMichal Meloun {
971a0a23564SMichal Meloun 
972a0a23564SMichal Meloun 	struct dc_softc *sc;
973a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
974a0a23564SMichal Meloun 	struct drm_gem_object *gem;
975a0a23564SMichal Meloun 	struct tegra_bo *bo;
976a0a23564SMichal Meloun 	int i;
977a0a23564SMichal Meloun 	uint32_t val, *src, *dst;
978a0a23564SMichal Meloun 
979a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
980a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
981a0a23564SMichal Meloun 
982a0a23564SMichal Meloun 	if (width != height)
983a0a23564SMichal Meloun 		return (-EINVAL);
984a0a23564SMichal Meloun 
985a0a23564SMichal Meloun 	switch (width) {
986a0a23564SMichal Meloun 	case 32:
987a0a23564SMichal Meloun 		val = CURSOR_SIZE(C32x32);
988a0a23564SMichal Meloun 		break;
989a0a23564SMichal Meloun 	case 64:
990a0a23564SMichal Meloun 		val = CURSOR_SIZE(C64x64);
991a0a23564SMichal Meloun 		break;
992a0a23564SMichal Meloun 	case 128:
993a0a23564SMichal Meloun 		val = CURSOR_SIZE(C128x128);
994a0a23564SMichal Meloun 		break;
995a0a23564SMichal Meloun 	case 256:
996a0a23564SMichal Meloun 		val = CURSOR_SIZE(C256x256);
997a0a23564SMichal Meloun 		break;
998a0a23564SMichal Meloun 	default:
999a0a23564SMichal Meloun 		return (-EINVAL);
1000a0a23564SMichal Meloun 	}
1001a0a23564SMichal Meloun 
1002a0a23564SMichal Meloun 	bo = NULL;
1003a0a23564SMichal Meloun 	gem = NULL;
1004a0a23564SMichal Meloun 	if (handle != 0) {
1005a0a23564SMichal Meloun 		gem = drm_gem_object_lookup(drm_crtc->dev, file, handle);
1006a0a23564SMichal Meloun 		if (gem == NULL)
1007a0a23564SMichal Meloun 			return (-ENOENT);
1008a0a23564SMichal Meloun 		bo = container_of(gem, struct tegra_bo, gem_obj);
1009a0a23564SMichal Meloun 	}
1010a0a23564SMichal Meloun 
1011a0a23564SMichal Meloun 	if (sc->cursor_gem != NULL) {
1012a0a23564SMichal Meloun 		drm_gem_object_unreference(sc->cursor_gem);
1013a0a23564SMichal Meloun 	}
1014a0a23564SMichal Meloun 	sc->cursor_gem = gem;
1015a0a23564SMichal Meloun 
1016a0a23564SMichal Meloun 	if (bo != NULL) {
1017a0a23564SMichal Meloun 		/*
1018a0a23564SMichal Meloun 		 * Copy cursor into cache and convert it from ARGB to RGBA.
1019a0a23564SMichal Meloun 		 * XXXX - this is broken by design - client can write to BO at
1020a0a23564SMichal Meloun 		 * any time. We can dedicate other window for cursor or switch
1021a0a23564SMichal Meloun 		 * to sw cursor in worst case.
1022a0a23564SMichal Meloun 		 */
1023a0a23564SMichal Meloun 		src = (uint32_t *)bo->vbase;
1024a0a23564SMichal Meloun 		dst = (uint32_t *)crtc->cursor_vbase;
1025a0a23564SMichal Meloun 		for (i = 0; i < width * height; i++)
1026a0a23564SMichal Meloun 			dst[i] = (src[i] << 8) | (src[i] >> 24);
1027a0a23564SMichal Meloun 
1028a0a23564SMichal Meloun 		val |= CURSOR_CLIP(CC_DISPLAY);
1029a0a23564SMichal Meloun 		val |= CURSOR_START_ADDR(crtc->cursor_pbase);
1030a0a23564SMichal Meloun 		WR4(sc, DC_DISP_CURSOR_START_ADDR, val);
1031a0a23564SMichal Meloun 
1032a0a23564SMichal Meloun 		val = RD4(sc, DC_DISP_BLEND_CURSOR_CONTROL);
1033a0a23564SMichal Meloun 		val &= ~CURSOR_DST_BLEND_FACTOR_SELECT(~0);
1034a0a23564SMichal Meloun 		val &= ~CURSOR_SRC_BLEND_FACTOR_SELECT(~0);
1035a0a23564SMichal Meloun 		val |= CURSOR_MODE_SELECT;
1036a0a23564SMichal Meloun 		val |= CURSOR_DST_BLEND_FACTOR_SELECT(DST_NEG_K1_TIMES_SRC);
1037a0a23564SMichal Meloun 		val |= CURSOR_SRC_BLEND_FACTOR_SELECT(SRC_BLEND_K1_TIMES_SRC);
1038a0a23564SMichal Meloun 		val |= CURSOR_ALPHA(~0);
1039a0a23564SMichal Meloun 		WR4(sc, DC_DISP_BLEND_CURSOR_CONTROL, val);
1040a0a23564SMichal Meloun 
1041a0a23564SMichal Meloun 		val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
1042a0a23564SMichal Meloun 		val |= CURSOR_ENABLE;
1043a0a23564SMichal Meloun 		WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
1044a0a23564SMichal Meloun 	} else {
1045a0a23564SMichal Meloun 		val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
1046a0a23564SMichal Meloun 		val &= ~CURSOR_ENABLE;
1047a0a23564SMichal Meloun 		WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
1048a0a23564SMichal Meloun 	}
1049a0a23564SMichal Meloun 
1050a0a23564SMichal Meloun 	/* XXX This fixes cursor underflow issues, but why ?  */
1051a0a23564SMichal Meloun 	WR4(sc, DC_DISP_CURSOR_UNDERFLOW_CTRL, CURSOR_UFLOW_CYA);
1052a0a23564SMichal Meloun 
1053a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | CURSOR_UPDATE );
1054a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | CURSOR_ACT_REQ);
1055a0a23564SMichal Meloun 	return (0);
1056a0a23564SMichal Meloun }
1057a0a23564SMichal Meloun 
1058a0a23564SMichal Meloun static int
1059a0a23564SMichal Meloun dc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
1060a0a23564SMichal Meloun {
1061a0a23564SMichal Meloun 	struct dc_softc *sc;
1062a0a23564SMichal Meloun 	struct tegra_crtc *crtc;
1063a0a23564SMichal Meloun 
1064a0a23564SMichal Meloun 	crtc = container_of(drm_crtc, struct tegra_crtc, drm_crtc);
1065a0a23564SMichal Meloun 	sc = device_get_softc(crtc->dev);
1066a0a23564SMichal Meloun 	WR4(sc, DC_DISP_CURSOR_POSITION, CURSOR_POSITION(x, y));
1067a0a23564SMichal Meloun 
1068a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_UPDATE);
1069a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, CURSOR_ACT_REQ);
1070a0a23564SMichal Meloun 
1071a0a23564SMichal Meloun 	return (0);
1072a0a23564SMichal Meloun }
1073a0a23564SMichal Meloun 
1074a0a23564SMichal Meloun static void
1075a0a23564SMichal Meloun dc_destroy(struct drm_crtc *crtc)
1076a0a23564SMichal Meloun {
1077a0a23564SMichal Meloun 
1078a0a23564SMichal Meloun 	drm_crtc_cleanup(crtc);
1079a0a23564SMichal Meloun 	memset(crtc, 0, sizeof(*crtc));
1080a0a23564SMichal Meloun }
1081a0a23564SMichal Meloun 
1082a0a23564SMichal Meloun static const struct drm_crtc_funcs dc_crtc_funcs = {
1083a0a23564SMichal Meloun 	.page_flip = dc_page_flip,
1084a0a23564SMichal Meloun 	.cursor_set = dc_cursor_set,
1085a0a23564SMichal Meloun 	.cursor_move = dc_cursor_move,
1086a0a23564SMichal Meloun 	.set_config = drm_crtc_helper_set_config,
1087a0a23564SMichal Meloun 	.destroy = dc_destroy,
1088a0a23564SMichal Meloun };
1089a0a23564SMichal Meloun 
1090a0a23564SMichal Meloun /* -------------------------------------------------------------------
1091a0a23564SMichal Meloun  *
1092a0a23564SMichal Meloun  *    Bus and infrastructure.
1093a0a23564SMichal Meloun  *
1094a0a23564SMichal Meloun  */
1095a0a23564SMichal Meloun static int
1096a0a23564SMichal Meloun dc_init_planes(struct dc_softc *sc, struct tegra_drm *drm)
1097a0a23564SMichal Meloun {
1098a0a23564SMichal Meloun 	int i, rv;
1099a0a23564SMichal Meloun 	struct tegra_plane *plane;
1100a0a23564SMichal Meloun 
1101a0a23564SMichal Meloun 	rv = 0;
1102a0a23564SMichal Meloun 	for (i = 0; i < DC_MAX_PLANES; i++) {
1103a0a23564SMichal Meloun 		plane = malloc(sizeof(*plane), DRM_MEM_KMS, M_WAITOK | M_ZERO);
1104a0a23564SMichal Meloun 		plane->index = i + 1;
1105a0a23564SMichal Meloun 		rv = drm_plane_init(&drm->drm_dev, &plane->drm_plane,
1106a0a23564SMichal Meloun 		    1 << sc->tegra_crtc.nvidia_head, &dc_plane_funcs,
1107a0a23564SMichal Meloun 		    dc_plane_formats, nitems(dc_plane_formats), false);
1108a0a23564SMichal Meloun 		if (rv != 0) {
1109a0a23564SMichal Meloun 			free(plane, DRM_MEM_KMS);
1110a0a23564SMichal Meloun 			return (rv);
1111a0a23564SMichal Meloun 		}
1112a0a23564SMichal Meloun 	}
1113a0a23564SMichal Meloun 	return 0;
1114a0a23564SMichal Meloun }
1115a0a23564SMichal Meloun 
1116a0a23564SMichal Meloun static void
1117a0a23564SMichal Meloun dc_display_enable(device_t dev, bool enable)
1118a0a23564SMichal Meloun {
1119a0a23564SMichal Meloun 	struct dc_softc *sc;
1120a0a23564SMichal Meloun 	uint32_t val;
1121a0a23564SMichal Meloun 
1122a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1123a0a23564SMichal Meloun 
1124a0a23564SMichal Meloun 	/* Set display mode */
1125a0a23564SMichal Meloun 	val = enable ? CTRL_MODE_C_DISPLAY: CTRL_MODE_STOP;
1126a0a23564SMichal Meloun 	WR4(sc, DC_CMD_DISPLAY_COMMAND, DISPLAY_CTRL_MODE(val));
1127a0a23564SMichal Meloun 
1128a0a23564SMichal Meloun 	/* and commit it*/
1129a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_UPDATE);
1130a0a23564SMichal Meloun 	WR4(sc, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ);
1131a0a23564SMichal Meloun }
1132a0a23564SMichal Meloun 
1133a0a23564SMichal Meloun static void
1134a0a23564SMichal Meloun dc_hdmi_enable(device_t dev, bool enable)
1135a0a23564SMichal Meloun {
1136a0a23564SMichal Meloun 	struct dc_softc *sc;
1137a0a23564SMichal Meloun 	uint32_t val;
1138a0a23564SMichal Meloun 
1139a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1140a0a23564SMichal Meloun 
1141a0a23564SMichal Meloun 	val = RD4(sc, DC_DISP_DISP_WIN_OPTIONS);
1142a0a23564SMichal Meloun 	if (enable)
1143a0a23564SMichal Meloun 		val |= HDMI_ENABLE;
1144a0a23564SMichal Meloun 	else
1145a0a23564SMichal Meloun 		val &= ~HDMI_ENABLE;
1146a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_WIN_OPTIONS, val);
1147a0a23564SMichal Meloun 
1148a0a23564SMichal Meloun }
1149a0a23564SMichal Meloun 
1150a0a23564SMichal Meloun static void
1151a0a23564SMichal Meloun dc_setup_timing(device_t dev, int h_pulse_start)
1152a0a23564SMichal Meloun {
1153a0a23564SMichal Meloun 	struct dc_softc *sc;
1154a0a23564SMichal Meloun 
1155a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1156a0a23564SMichal Meloun 
1157a0a23564SMichal Meloun 	/* Setup display timing */
1158a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_TIMING_OPTIONS, VSYNC_H_POSITION(1));
1159a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_COLOR_CONTROL,
1160a0a23564SMichal Meloun 	    DITHER_CONTROL(DITHER_DISABLE) | BASE_COLOR_SIZE(SIZE_BASE888));
1161a0a23564SMichal Meloun 
1162a0a23564SMichal Meloun 	WR4(sc, DC_DISP_DISP_SIGNAL_OPTIONS0, H_PULSE2_ENABLE);
1163a0a23564SMichal Meloun 	WR4(sc, DC_DISP_H_PULSE2_CONTROL,
1164a0a23564SMichal Meloun 	    PULSE_CONTROL_QUAL(QUAL_VACTIVE) | PULSE_CONTROL_LAST(LAST_END_A));
1165a0a23564SMichal Meloun 
1166a0a23564SMichal Meloun 	WR4(sc, DC_DISP_H_PULSE2_POSITION_A,
1167a0a23564SMichal Meloun 	    PULSE_START(h_pulse_start) | PULSE_END(h_pulse_start + 8));
1168a0a23564SMichal Meloun }
1169a0a23564SMichal Meloun 
1170a0a23564SMichal Meloun static void
1171a0a23564SMichal Meloun dc_intr(void *arg)
1172a0a23564SMichal Meloun {
1173a0a23564SMichal Meloun 	struct dc_softc *sc;
1174a0a23564SMichal Meloun 	uint32_t status;
1175a0a23564SMichal Meloun 
1176a0a23564SMichal Meloun 	sc = arg;
1177a0a23564SMichal Meloun 
1178a0a23564SMichal Meloun 	/* Confirm interrupt */
1179a0a23564SMichal Meloun 	status = RD4(sc, DC_CMD_INT_STATUS);
1180a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_STATUS, status);
1181a0a23564SMichal Meloun 	if (status & VBLANK_INT) {
1182a0a23564SMichal Meloun 		drm_handle_vblank(sc->tegra_crtc.drm_crtc.dev,
1183a0a23564SMichal Meloun 		    sc->tegra_crtc.nvidia_head);
1184a0a23564SMichal Meloun 		dc_finish_page_flip(sc);
1185a0a23564SMichal Meloun 	}
1186a0a23564SMichal Meloun }
1187a0a23564SMichal Meloun 
1188a0a23564SMichal Meloun static int
1189a0a23564SMichal Meloun dc_init_client(device_t dev, device_t host1x, struct tegra_drm *drm)
1190a0a23564SMichal Meloun {
1191a0a23564SMichal Meloun 	struct dc_softc *sc;
1192a0a23564SMichal Meloun 	int rv;
1193a0a23564SMichal Meloun 
1194a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1195a0a23564SMichal Meloun 
1196a0a23564SMichal Meloun 	if (drm->pitch_align < sc->pitch_align)
1197a0a23564SMichal Meloun 		drm->pitch_align = sc->pitch_align;
1198a0a23564SMichal Meloun 
1199a0a23564SMichal Meloun 	drm_crtc_init(&drm->drm_dev, &sc->tegra_crtc.drm_crtc, &dc_crtc_funcs);
1200a0a23564SMichal Meloun 	drm_mode_crtc_set_gamma_size(&sc->tegra_crtc.drm_crtc, 256);
1201a0a23564SMichal Meloun 	drm_crtc_helper_add(&sc->tegra_crtc.drm_crtc, &dc_crtc_helper_funcs);
1202a0a23564SMichal Meloun 
1203a0a23564SMichal Meloun 	rv = dc_init_planes(sc, drm);
1204a0a23564SMichal Meloun 	if (rv!= 0){
1205a0a23564SMichal Meloun 		device_printf(dev, "Cannot init planes\n");
1206a0a23564SMichal Meloun 		return (rv);
1207a0a23564SMichal Meloun 	}
1208a0a23564SMichal Meloun 
1209a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_TYPE,
1210a0a23564SMichal Meloun 	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
1211a0a23564SMichal Meloun 	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
1212a0a23564SMichal Meloun 
1213a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_POLARITY,
1214a0a23564SMichal Meloun 	    WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
1215a0a23564SMichal Meloun 	    WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT);
1216a0a23564SMichal Meloun 
1217a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_ENABLE, 0);
1218a0a23564SMichal Meloun 	WR4(sc, DC_CMD_INT_MASK, 0);
1219a0a23564SMichal Meloun 
1220a0a23564SMichal Meloun 	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
1221a0a23564SMichal Meloun 	    NULL, dc_intr, sc, &sc->irq_ih);
1222a0a23564SMichal Meloun 	if (rv != 0) {
1223a0a23564SMichal Meloun 		device_printf(dev, "Cannot register interrupt handler\n");
1224a0a23564SMichal Meloun 		return (rv);
1225a0a23564SMichal Meloun 	}
1226a0a23564SMichal Meloun 
1227a0a23564SMichal Meloun 	/* allocate memory for cursor cache */
122844d0efb2SAlan Cox 	sc->tegra_crtc.cursor_vbase = kmem_alloc_contig(256 * 256 * 4,
122944d0efb2SAlan Cox 	    M_WAITOK | M_ZERO, 0, -1UL, PAGE_SIZE, 0,
123044d0efb2SAlan Cox 	    VM_MEMATTR_WRITE_COMBINING);
1231f49fd63aSJohn Baldwin 	sc->tegra_crtc.cursor_pbase =
1232f49fd63aSJohn Baldwin 	    vtophys((uintptr_t)sc->tegra_crtc.cursor_vbase);
1233a0a23564SMichal Meloun 	return (0);
1234a0a23564SMichal Meloun }
1235a0a23564SMichal Meloun 
1236a0a23564SMichal Meloun static int
1237a0a23564SMichal Meloun dc_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm)
1238a0a23564SMichal Meloun {
1239a0a23564SMichal Meloun 	struct dc_softc *sc;
1240a0a23564SMichal Meloun 
1241a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1242a0a23564SMichal Meloun 
1243a0a23564SMichal Meloun 	if (sc->irq_ih != NULL)
1244a0a23564SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1245a0a23564SMichal Meloun 	sc->irq_ih = NULL;
1246a0a23564SMichal Meloun 
1247a0a23564SMichal Meloun 	return (0);
1248a0a23564SMichal Meloun }
1249a0a23564SMichal Meloun 
1250a0a23564SMichal Meloun static int
1251a0a23564SMichal Meloun get_fdt_resources(struct dc_softc *sc, phandle_t node)
1252a0a23564SMichal Meloun {
1253a0a23564SMichal Meloun 	int rv;
1254a0a23564SMichal Meloun 
1255a0a23564SMichal Meloun 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "dc", &sc->hwreset_dc);
1256a0a23564SMichal Meloun 	if (rv != 0) {
1257a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot get 'dc' reset\n");
1258a0a23564SMichal Meloun 		return (rv);
1259a0a23564SMichal Meloun 	}
1260a0a23564SMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent);
1261a0a23564SMichal Meloun 	if (rv != 0) {
1262a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot get 'parent' clock\n");
1263a0a23564SMichal Meloun 		return (rv);
1264a0a23564SMichal Meloun 	}
1265a0a23564SMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "dc", &sc->clk_dc);
1266a0a23564SMichal Meloun 	if (rv != 0) {
1267a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot get 'dc' clock\n");
1268a0a23564SMichal Meloun 		return (rv);
1269a0a23564SMichal Meloun 	}
1270a0a23564SMichal Meloun 
1271a0a23564SMichal Meloun 	rv = OF_getencprop(node, "nvidia,head", &sc->tegra_crtc.nvidia_head,
1272a0a23564SMichal Meloun 	    sizeof(sc->tegra_crtc.nvidia_head));
1273a0a23564SMichal Meloun 	if (rv <= 0) {
1274a0a23564SMichal Meloun 		device_printf(sc->dev,
1275a0a23564SMichal Meloun 		    "Cannot get 'nvidia,head' property\n");
1276a0a23564SMichal Meloun 		return (rv);
1277a0a23564SMichal Meloun 	}
1278a0a23564SMichal Meloun 	return (0);
1279a0a23564SMichal Meloun }
1280a0a23564SMichal Meloun 
1281a0a23564SMichal Meloun static int
1282a0a23564SMichal Meloun enable_fdt_resources(struct dc_softc *sc)
1283a0a23564SMichal Meloun {
1284a0a23564SMichal Meloun 	int id, rv;
1285a0a23564SMichal Meloun 
1286a0a23564SMichal Meloun 	rv = clk_set_parent_by_clk(sc->clk_dc, sc->clk_parent);
1287a0a23564SMichal Meloun 	if (rv != 0) {
1288a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot set parent for 'dc' clock\n");
1289a0a23564SMichal Meloun 		return (rv);
1290a0a23564SMichal Meloun 	}
1291a0a23564SMichal Meloun 
1292a0a23564SMichal Meloun 	id = (sc->tegra_crtc.nvidia_head == 0) ?
1293a0a23564SMichal Meloun 	    TEGRA_POWERGATE_DIS: TEGRA_POWERGATE_DISB;
1294a0a23564SMichal Meloun 	rv = tegra_powergate_sequence_power_up(id, sc->clk_dc, sc->hwreset_dc);
1295a0a23564SMichal Meloun 	if (rv != 0) {
1296a0a23564SMichal Meloun 		device_printf(sc->dev, "Cannot enable 'DIS' powergate\n");
1297a0a23564SMichal Meloun 		return (rv);
1298a0a23564SMichal Meloun 	}
1299a0a23564SMichal Meloun 
1300a0a23564SMichal Meloun 	return (0);
1301a0a23564SMichal Meloun }
1302a0a23564SMichal Meloun 
1303a0a23564SMichal Meloun static int
1304a0a23564SMichal Meloun dc_probe(device_t dev)
1305a0a23564SMichal Meloun {
1306a0a23564SMichal Meloun 
1307a0a23564SMichal Meloun 	if (!ofw_bus_status_okay(dev))
1308a0a23564SMichal Meloun 		return (ENXIO);
1309a0a23564SMichal Meloun 
1310a0a23564SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
1311a0a23564SMichal Meloun 		return (ENXIO);
1312a0a23564SMichal Meloun 
1313a0a23564SMichal Meloun 	device_set_desc(dev, "Tegra Display Controller");
1314a0a23564SMichal Meloun 	return (BUS_PROBE_DEFAULT);
1315a0a23564SMichal Meloun }
1316a0a23564SMichal Meloun 
1317a0a23564SMichal Meloun static int
1318a0a23564SMichal Meloun dc_attach(device_t dev)
1319a0a23564SMichal Meloun {
1320a0a23564SMichal Meloun 	struct dc_softc *sc;
1321a0a23564SMichal Meloun 	phandle_t node;
1322a0a23564SMichal Meloun 	int rid, rv;
1323a0a23564SMichal Meloun 
1324a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1325a0a23564SMichal Meloun 	sc->dev = dev;
1326a0a23564SMichal Meloun 	sc->tegra_crtc.dev = dev;
1327a0a23564SMichal Meloun 
1328a0a23564SMichal Meloun 	node = ofw_bus_get_node(sc->dev);
1329a0a23564SMichal Meloun 	LOCK_INIT(sc);
1330a0a23564SMichal Meloun 
1331a0a23564SMichal Meloun 	rid = 0;
1332a0a23564SMichal Meloun 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1333a0a23564SMichal Meloun 	    RF_ACTIVE);
1334a0a23564SMichal Meloun 	if (sc->mem_res == NULL) {
1335a0a23564SMichal Meloun 		device_printf(dev, "Cannot allocate memory resources\n");
1336a0a23564SMichal Meloun 		goto fail;
1337a0a23564SMichal Meloun 	}
1338a0a23564SMichal Meloun 
1339a0a23564SMichal Meloun 	rid = 0;
1340a0a23564SMichal Meloun 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
1341a0a23564SMichal Meloun 	if (sc->irq_res == NULL) {
1342a0a23564SMichal Meloun 		device_printf(dev, "Cannot allocate IRQ resources\n");
1343a0a23564SMichal Meloun 		goto fail;
1344a0a23564SMichal Meloun 	}
1345a0a23564SMichal Meloun 
1346a0a23564SMichal Meloun 	rv = get_fdt_resources(sc, node);
1347a0a23564SMichal Meloun 	if (rv != 0) {
1348a0a23564SMichal Meloun 		device_printf(dev, "Cannot parse FDT resources\n");
1349a0a23564SMichal Meloun 		goto fail;
1350a0a23564SMichal Meloun 	}
1351a0a23564SMichal Meloun 	rv = enable_fdt_resources(sc);
1352a0a23564SMichal Meloun 	if (rv != 0) {
1353a0a23564SMichal Meloun 		device_printf(dev, "Cannot enable FDT resources\n");
1354a0a23564SMichal Meloun 		goto fail;
1355a0a23564SMichal Meloun 	}
1356a0a23564SMichal Meloun 
1357a0a23564SMichal Meloun 	/*
1358a0a23564SMichal Meloun 	 * Tegra124
1359a0a23564SMichal Meloun 	 *  -  64 for RGB modes
1360a0a23564SMichal Meloun 	 *  - 128 for YUV planar modes
1361a0a23564SMichal Meloun 	 *  - 256 for block linear modes
1362a0a23564SMichal Meloun 	 */
1363a0a23564SMichal Meloun 	sc->pitch_align = 256;
1364a0a23564SMichal Meloun 
1365a0a23564SMichal Meloun 	rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1366a0a23564SMichal Meloun 	if (rv != 0) {
1367a0a23564SMichal Meloun 		device_printf(dev, "Cannot register DRM device\n");
1368a0a23564SMichal Meloun 		goto fail;
1369a0a23564SMichal Meloun 	}
1370a0a23564SMichal Meloun 
1371*18250ec6SJohn Baldwin 	bus_attach_children(dev);
1372*18250ec6SJohn Baldwin 	return (0);
1373a0a23564SMichal Meloun 
1374a0a23564SMichal Meloun fail:
1375a0a23564SMichal Meloun 	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1376a0a23564SMichal Meloun 	if (sc->irq_ih != NULL)
1377a0a23564SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1378a0a23564SMichal Meloun 	if (sc->clk_parent != NULL)
1379a0a23564SMichal Meloun 		clk_release(sc->clk_parent);
1380a0a23564SMichal Meloun 	if (sc->clk_dc != NULL)
1381a0a23564SMichal Meloun 		clk_release(sc->clk_dc);
1382a0a23564SMichal Meloun 	if (sc->hwreset_dc != NULL)
1383a0a23564SMichal Meloun 		hwreset_release(sc->hwreset_dc);
1384a0a23564SMichal Meloun 	if (sc->irq_res != NULL)
1385a0a23564SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1386a0a23564SMichal Meloun 	if (sc->mem_res != NULL)
1387a0a23564SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
1388a0a23564SMichal Meloun 	LOCK_DESTROY(sc);
1389a0a23564SMichal Meloun 
1390a0a23564SMichal Meloun 	return (ENXIO);
1391a0a23564SMichal Meloun }
1392a0a23564SMichal Meloun 
1393a0a23564SMichal Meloun static int
1394a0a23564SMichal Meloun dc_detach(device_t dev)
1395a0a23564SMichal Meloun {
1396a0a23564SMichal Meloun 	struct dc_softc *sc;
1397d412c076SJohn Baldwin 	int error;
1398d412c076SJohn Baldwin 
1399d412c076SJohn Baldwin 	error = bus_generic_detach(dev);
1400d412c076SJohn Baldwin 	if (error != 0)
1401d412c076SJohn Baldwin 		return (error);
1402a0a23564SMichal Meloun 
1403a0a23564SMichal Meloun 	sc = device_get_softc(dev);
1404a0a23564SMichal Meloun 
1405a0a23564SMichal Meloun 	TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev);
1406a0a23564SMichal Meloun 
1407a0a23564SMichal Meloun 	if (sc->irq_ih != NULL)
1408a0a23564SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
1409a0a23564SMichal Meloun 	if (sc->clk_parent != NULL)
1410a0a23564SMichal Meloun 		clk_release(sc->clk_parent);
1411a0a23564SMichal Meloun 	if (sc->clk_dc != NULL)
1412a0a23564SMichal Meloun 		clk_release(sc->clk_dc);
1413a0a23564SMichal Meloun 	if (sc->hwreset_dc != NULL)
1414a0a23564SMichal Meloun 		hwreset_release(sc->hwreset_dc);
1415a0a23564SMichal Meloun 	if (sc->irq_res != NULL)
1416a0a23564SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1417a0a23564SMichal Meloun 	if (sc->mem_res != NULL)
1418a0a23564SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
1419a0a23564SMichal Meloun 	LOCK_DESTROY(sc);
1420a0a23564SMichal Meloun 
1421d412c076SJohn Baldwin 	return (0);
1422a0a23564SMichal Meloun }
1423a0a23564SMichal Meloun 
1424a0a23564SMichal Meloun static device_method_t tegra_dc_methods[] = {
1425a0a23564SMichal Meloun 	/* Device interface */
1426a0a23564SMichal Meloun 	DEVMETHOD(device_probe,			dc_probe),
1427a0a23564SMichal Meloun 	DEVMETHOD(device_attach,		dc_attach),
1428a0a23564SMichal Meloun 	DEVMETHOD(device_detach,		dc_detach),
1429a0a23564SMichal Meloun 
1430a0a23564SMichal Meloun 	/* tegra drm interface */
1431a0a23564SMichal Meloun 	DEVMETHOD(tegra_drm_init_client,	dc_init_client),
1432a0a23564SMichal Meloun 	DEVMETHOD(tegra_drm_exit_client,	dc_exit_client),
1433a0a23564SMichal Meloun 
1434a0a23564SMichal Meloun 	/* tegra dc interface */
1435a0a23564SMichal Meloun 	DEVMETHOD(tegra_dc_display_enable,	dc_display_enable),
1436a0a23564SMichal Meloun 	DEVMETHOD(tegra_dc_hdmi_enable,		dc_hdmi_enable),
1437a0a23564SMichal Meloun 	DEVMETHOD(tegra_dc_setup_timing,	dc_setup_timing),
1438a0a23564SMichal Meloun 
1439a0a23564SMichal Meloun 	DEVMETHOD_END
1440a0a23564SMichal Meloun };
1441a0a23564SMichal Meloun 
1442a0a23564SMichal Meloun DEFINE_CLASS_0(tegra_dc, tegra_dc_driver, tegra_dc_methods,
1443a0a23564SMichal Meloun     sizeof(struct dc_softc));
1444289f133bSJohn Baldwin DRIVER_MODULE(tegra_dc, host1x, tegra_dc_driver, NULL, NULL);
1445