xref: /dflybsd-src/sys/dev/drm/i915/intel_overlay.c (revision 5c6c6f233992d9a8b1765176aeec0a54588e65c7)
1e3adcf8fSFrançois Tigeot /*
2e3adcf8fSFrançois Tigeot  * Copyright © 2009
3e3adcf8fSFrançois Tigeot  *
4e3adcf8fSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
5e3adcf8fSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
6e3adcf8fSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
7e3adcf8fSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e3adcf8fSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
9e3adcf8fSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
10e3adcf8fSFrançois Tigeot  *
11e3adcf8fSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
12e3adcf8fSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
13e3adcf8fSFrançois Tigeot  * Software.
14e3adcf8fSFrançois Tigeot  *
15e3adcf8fSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16e3adcf8fSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17e3adcf8fSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18e3adcf8fSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19e3adcf8fSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20e3adcf8fSFrançois Tigeot  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21e3adcf8fSFrançois Tigeot  * SOFTWARE.
22e3adcf8fSFrançois Tigeot  *
23e3adcf8fSFrançois Tigeot  * Authors:
24e3adcf8fSFrançois Tigeot  *    Daniel Vetter <daniel@ffwll.ch>
25e3adcf8fSFrançois Tigeot  *
26e3adcf8fSFrançois Tigeot  * Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
27e3adcf8fSFrançois Tigeot  * $FreeBSD: src/sys/dev/drm2/i915/intel_overlay.c,v 1.1 2012/05/22 11:07:44 kib Exp $
28e3adcf8fSFrançois Tigeot  */
29e3adcf8fSFrançois Tigeot 
3018e26a6dSFrançois Tigeot #include <drm/drmP.h>
31*5c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
32e3adcf8fSFrançois Tigeot #include "i915_drv.h"
33e3adcf8fSFrançois Tigeot #include "i915_reg.h"
34e3adcf8fSFrançois Tigeot #include "intel_drv.h"
35e3adcf8fSFrançois Tigeot 
36e3adcf8fSFrançois Tigeot /* Limits for overlay size. According to intel doc, the real limits are:
37e3adcf8fSFrançois Tigeot  * Y width: 4095, UV width (planar): 2047, Y height: 2047,
38e3adcf8fSFrançois Tigeot  * UV width (planar): * 1023. But the xorg thinks 2048 for height and width. Use
39e3adcf8fSFrançois Tigeot  * the mininum of both.  */
40e3adcf8fSFrançois Tigeot #define IMAGE_MAX_WIDTH		2048
41e3adcf8fSFrançois Tigeot #define IMAGE_MAX_HEIGHT	2046 /* 2 * 1023 */
42e3adcf8fSFrançois Tigeot /* on 830 and 845 these large limits result in the card hanging */
43e3adcf8fSFrançois Tigeot #define IMAGE_MAX_WIDTH_LEGACY	1024
44e3adcf8fSFrançois Tigeot #define IMAGE_MAX_HEIGHT_LEGACY	1088
45e3adcf8fSFrançois Tigeot 
46e3adcf8fSFrançois Tigeot /* overlay register definitions */
47e3adcf8fSFrançois Tigeot /* OCMD register */
48e3adcf8fSFrançois Tigeot #define OCMD_TILED_SURFACE	(0x1<<19)
49e3adcf8fSFrançois Tigeot #define OCMD_MIRROR_MASK	(0x3<<17)
50e3adcf8fSFrançois Tigeot #define OCMD_MIRROR_MODE	(0x3<<17)
51e3adcf8fSFrançois Tigeot #define OCMD_MIRROR_HORIZONTAL	(0x1<<17)
52e3adcf8fSFrançois Tigeot #define OCMD_MIRROR_VERTICAL	(0x2<<17)
53e3adcf8fSFrançois Tigeot #define OCMD_MIRROR_BOTH	(0x3<<17)
54e3adcf8fSFrançois Tigeot #define OCMD_BYTEORDER_MASK	(0x3<<14) /* zero for YUYV or FOURCC YUY2 */
55e3adcf8fSFrançois Tigeot #define OCMD_UV_SWAP		(0x1<<14) /* YVYU */
56e3adcf8fSFrançois Tigeot #define OCMD_Y_SWAP		(0x2<<14) /* UYVY or FOURCC UYVY */
57e3adcf8fSFrançois Tigeot #define OCMD_Y_AND_UV_SWAP	(0x3<<14) /* VYUY */
58e3adcf8fSFrançois Tigeot #define OCMD_SOURCE_FORMAT_MASK (0xf<<10)
59e3adcf8fSFrançois Tigeot #define OCMD_RGB_888		(0x1<<10) /* not in i965 Intel docs */
60e3adcf8fSFrançois Tigeot #define OCMD_RGB_555		(0x2<<10) /* not in i965 Intel docs */
61e3adcf8fSFrançois Tigeot #define OCMD_RGB_565		(0x3<<10) /* not in i965 Intel docs */
62e3adcf8fSFrançois Tigeot #define OCMD_YUV_422_PACKED	(0x8<<10)
63e3adcf8fSFrançois Tigeot #define OCMD_YUV_411_PACKED	(0x9<<10) /* not in i965 Intel docs */
64e3adcf8fSFrançois Tigeot #define OCMD_YUV_420_PLANAR	(0xc<<10)
65e3adcf8fSFrançois Tigeot #define OCMD_YUV_422_PLANAR	(0xd<<10)
66e3adcf8fSFrançois Tigeot #define OCMD_YUV_410_PLANAR	(0xe<<10) /* also 411 */
67e3adcf8fSFrançois Tigeot #define OCMD_TVSYNCFLIP_PARITY	(0x1<<9)
68e3adcf8fSFrançois Tigeot #define OCMD_TVSYNCFLIP_ENABLE	(0x1<<7)
69e3adcf8fSFrançois Tigeot #define OCMD_BUF_TYPE_MASK	(0x1<<5)
70e3adcf8fSFrançois Tigeot #define OCMD_BUF_TYPE_FRAME	(0x0<<5)
71e3adcf8fSFrançois Tigeot #define OCMD_BUF_TYPE_FIELD	(0x1<<5)
72e3adcf8fSFrançois Tigeot #define OCMD_TEST_MODE		(0x1<<4)
73e3adcf8fSFrançois Tigeot #define OCMD_BUFFER_SELECT	(0x3<<2)
74e3adcf8fSFrançois Tigeot #define OCMD_BUFFER0		(0x0<<2)
75e3adcf8fSFrançois Tigeot #define OCMD_BUFFER1		(0x1<<2)
76e3adcf8fSFrançois Tigeot #define OCMD_FIELD_SELECT	(0x1<<2)
77e3adcf8fSFrançois Tigeot #define OCMD_FIELD0		(0x0<<1)
78e3adcf8fSFrançois Tigeot #define OCMD_FIELD1		(0x1<<1)
79e3adcf8fSFrançois Tigeot #define OCMD_ENABLE		(0x1<<0)
80e3adcf8fSFrançois Tigeot 
81e3adcf8fSFrançois Tigeot /* OCONFIG register */
82e3adcf8fSFrançois Tigeot #define OCONF_PIPE_MASK		(0x1<<18)
83e3adcf8fSFrançois Tigeot #define OCONF_PIPE_A		(0x0<<18)
84e3adcf8fSFrançois Tigeot #define OCONF_PIPE_B		(0x1<<18)
85e3adcf8fSFrançois Tigeot #define OCONF_GAMMA2_ENABLE	(0x1<<16)
86e3adcf8fSFrançois Tigeot #define OCONF_CSC_MODE_BT601	(0x0<<5)
87e3adcf8fSFrançois Tigeot #define OCONF_CSC_MODE_BT709	(0x1<<5)
88e3adcf8fSFrançois Tigeot #define OCONF_CSC_BYPASS	(0x1<<4)
89e3adcf8fSFrançois Tigeot #define OCONF_CC_OUT_8BIT	(0x1<<3)
90e3adcf8fSFrançois Tigeot #define OCONF_TEST_MODE		(0x1<<2)
91e3adcf8fSFrançois Tigeot #define OCONF_THREE_LINE_BUFFER	(0x1<<0)
92e3adcf8fSFrançois Tigeot #define OCONF_TWO_LINE_BUFFER	(0x0<<0)
93e3adcf8fSFrançois Tigeot 
94e3adcf8fSFrançois Tigeot /* DCLRKM (dst-key) register */
95e3adcf8fSFrançois Tigeot #define DST_KEY_ENABLE		(0x1<<31)
96e3adcf8fSFrançois Tigeot #define CLK_RGB24_MASK		0x0
97e3adcf8fSFrançois Tigeot #define CLK_RGB16_MASK		0x070307
98e3adcf8fSFrançois Tigeot #define CLK_RGB15_MASK		0x070707
99e3adcf8fSFrançois Tigeot #define CLK_RGB8I_MASK		0xffffff
100e3adcf8fSFrançois Tigeot 
101e3adcf8fSFrançois Tigeot #define RGB16_TO_COLORKEY(c) \
102e3adcf8fSFrançois Tigeot 	(((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3))
103e3adcf8fSFrançois Tigeot #define RGB15_TO_COLORKEY(c) \
104e3adcf8fSFrançois Tigeot 	(((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3))
105e3adcf8fSFrançois Tigeot 
106e3adcf8fSFrançois Tigeot /* overlay flip addr flag */
107e3adcf8fSFrançois Tigeot #define OFC_UPDATE		0x1
108e3adcf8fSFrançois Tigeot 
109e3adcf8fSFrançois Tigeot /* polyphase filter coefficients */
110e3adcf8fSFrançois Tigeot #define N_HORIZ_Y_TAPS          5
111e3adcf8fSFrançois Tigeot #define N_VERT_Y_TAPS           3
112e3adcf8fSFrançois Tigeot #define N_HORIZ_UV_TAPS         3
113e3adcf8fSFrançois Tigeot #define N_VERT_UV_TAPS          3
114e3adcf8fSFrançois Tigeot #define N_PHASES                17
115e3adcf8fSFrançois Tigeot #define MAX_TAPS                5
116e3adcf8fSFrançois Tigeot 
117e3adcf8fSFrançois Tigeot /* memory bufferd overlay registers */
118e3adcf8fSFrançois Tigeot struct overlay_registers {
119e3adcf8fSFrançois Tigeot 	u32 OBUF_0Y;
120e3adcf8fSFrançois Tigeot 	u32 OBUF_1Y;
121e3adcf8fSFrançois Tigeot 	u32 OBUF_0U;
122e3adcf8fSFrançois Tigeot 	u32 OBUF_0V;
123e3adcf8fSFrançois Tigeot 	u32 OBUF_1U;
124e3adcf8fSFrançois Tigeot 	u32 OBUF_1V;
125e3adcf8fSFrançois Tigeot 	u32 OSTRIDE;
126e3adcf8fSFrançois Tigeot 	u32 YRGB_VPH;
127e3adcf8fSFrançois Tigeot 	u32 UV_VPH;
128e3adcf8fSFrançois Tigeot 	u32 HORZ_PH;
129e3adcf8fSFrançois Tigeot 	u32 INIT_PHS;
130e3adcf8fSFrançois Tigeot 	u32 DWINPOS;
131e3adcf8fSFrançois Tigeot 	u32 DWINSZ;
132e3adcf8fSFrançois Tigeot 	u32 SWIDTH;
133e3adcf8fSFrançois Tigeot 	u32 SWIDTHSW;
134e3adcf8fSFrançois Tigeot 	u32 SHEIGHT;
135e3adcf8fSFrançois Tigeot 	u32 YRGBSCALE;
136e3adcf8fSFrançois Tigeot 	u32 UVSCALE;
137e3adcf8fSFrançois Tigeot 	u32 OCLRC0;
138e3adcf8fSFrançois Tigeot 	u32 OCLRC1;
139e3adcf8fSFrançois Tigeot 	u32 DCLRKV;
140e3adcf8fSFrançois Tigeot 	u32 DCLRKM;
141e3adcf8fSFrançois Tigeot 	u32 SCLRKVH;
142e3adcf8fSFrançois Tigeot 	u32 SCLRKVL;
143e3adcf8fSFrançois Tigeot 	u32 SCLRKEN;
144e3adcf8fSFrançois Tigeot 	u32 OCONFIG;
145e3adcf8fSFrançois Tigeot 	u32 OCMD;
146e3adcf8fSFrançois Tigeot 	u32 RESERVED1; /* 0x6C */
147e3adcf8fSFrançois Tigeot 	u32 OSTART_0Y;
148e3adcf8fSFrançois Tigeot 	u32 OSTART_1Y;
149e3adcf8fSFrançois Tigeot 	u32 OSTART_0U;
150e3adcf8fSFrançois Tigeot 	u32 OSTART_0V;
151e3adcf8fSFrançois Tigeot 	u32 OSTART_1U;
152e3adcf8fSFrançois Tigeot 	u32 OSTART_1V;
153e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_0Y;
154e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_1Y;
155e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_0U;
156e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_0V;
157e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_1U;
158e3adcf8fSFrançois Tigeot 	u32 OTILEOFF_1V;
159e3adcf8fSFrançois Tigeot 	u32 FASTHSCALE; /* 0xA0 */
160e3adcf8fSFrançois Tigeot 	u32 UVSCALEV; /* 0xA4 */
161e3adcf8fSFrançois Tigeot 	u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
162e3adcf8fSFrançois Tigeot 	u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
163e3adcf8fSFrançois Tigeot 	u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
164e3adcf8fSFrançois Tigeot 	u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
165e3adcf8fSFrançois Tigeot 	u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
166e3adcf8fSFrançois Tigeot 	u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
167e3adcf8fSFrançois Tigeot 	u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
168e3adcf8fSFrançois Tigeot 	u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
169e3adcf8fSFrançois Tigeot 	u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
170e3adcf8fSFrançois Tigeot };
171e3adcf8fSFrançois Tigeot 
172e3adcf8fSFrançois Tigeot struct intel_overlay {
173e3adcf8fSFrançois Tigeot 	struct drm_device *dev;
174e3adcf8fSFrançois Tigeot 	struct intel_crtc *crtc;
175e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *vid_bo;
176e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *old_vid_bo;
177e3adcf8fSFrançois Tigeot 	int active;
178e3adcf8fSFrançois Tigeot 	int pfit_active;
179e3adcf8fSFrançois Tigeot 	u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
180e3adcf8fSFrançois Tigeot 	u32 color_key;
181e3adcf8fSFrançois Tigeot 	u32 brightness, contrast, saturation;
182e3adcf8fSFrançois Tigeot 	u32 old_xscale, old_yscale;
183e3adcf8fSFrançois Tigeot 	/* register access */
184e3adcf8fSFrançois Tigeot 	u32 flip_addr;
185e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *reg_bo;
186e3adcf8fSFrançois Tigeot 	/* flip handling */
187e3adcf8fSFrançois Tigeot 	uint32_t last_flip_req;
188e3adcf8fSFrançois Tigeot 	void (*flip_tail)(struct intel_overlay *);
189e3adcf8fSFrançois Tigeot };
190e3adcf8fSFrançois Tigeot 
191e3adcf8fSFrançois Tigeot static struct overlay_registers *
192e3adcf8fSFrançois Tigeot intel_overlay_map_regs(struct intel_overlay *overlay)
193e3adcf8fSFrançois Tigeot {
194e3adcf8fSFrançois Tigeot 	struct overlay_registers *regs;
195e3adcf8fSFrançois Tigeot 
196e3adcf8fSFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) {
197e3adcf8fSFrançois Tigeot 		regs = overlay->reg_bo->phys_obj->handle->vaddr;
198e3adcf8fSFrançois Tigeot 	} else {
199e3adcf8fSFrançois Tigeot 		regs = pmap_mapdev_attr(overlay->dev->agp->base +
200e3adcf8fSFrançois Tigeot 		    overlay->reg_bo->gtt_offset, PAGE_SIZE,
201e3adcf8fSFrançois Tigeot 		    PAT_WRITE_COMBINING);
202e3adcf8fSFrançois Tigeot 	}
203e3adcf8fSFrançois Tigeot 	return (regs);
204e3adcf8fSFrançois Tigeot }
205e3adcf8fSFrançois Tigeot 
206e3adcf8fSFrançois Tigeot static void intel_overlay_unmap_regs(struct intel_overlay *overlay,
207e3adcf8fSFrançois Tigeot 				     struct overlay_registers *regs)
208e3adcf8fSFrançois Tigeot {
209e3adcf8fSFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev))
210e3adcf8fSFrançois Tigeot 		pmap_unmapdev((vm_offset_t)regs, PAGE_SIZE);
211e3adcf8fSFrançois Tigeot }
212e3adcf8fSFrançois Tigeot 
213e3adcf8fSFrançois Tigeot static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
214e3adcf8fSFrançois Tigeot 					 struct drm_i915_gem_request *request,
215e3adcf8fSFrançois Tigeot 					 void (*tail)(struct intel_overlay *))
216e3adcf8fSFrançois Tigeot {
217e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
218e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
219e3adcf8fSFrançois Tigeot 	int ret;
220e3adcf8fSFrançois Tigeot 
221e3adcf8fSFrançois Tigeot 	KASSERT(!overlay->last_flip_req, ("Overlay already has flip req"));
222e3adcf8fSFrançois Tigeot 	ret = i915_add_request(LP_RING(dev_priv), NULL, request);
223e3adcf8fSFrançois Tigeot 	if (ret) {
224e3adcf8fSFrançois Tigeot 		drm_free(request, DRM_I915_GEM);
225e3adcf8fSFrançois Tigeot 		return ret;
226e3adcf8fSFrançois Tigeot 	}
227e3adcf8fSFrançois Tigeot 	overlay->last_flip_req = request->seqno;
228e3adcf8fSFrançois Tigeot 	overlay->flip_tail = tail;
229e3adcf8fSFrançois Tigeot 	ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req,
230e3adcf8fSFrançois Tigeot 				true);
231e3adcf8fSFrançois Tigeot 	if (ret)
232e3adcf8fSFrançois Tigeot 		return ret;
233e3adcf8fSFrançois Tigeot 
234e3adcf8fSFrançois Tigeot 	overlay->last_flip_req = 0;
235e3adcf8fSFrançois Tigeot 	return 0;
236e3adcf8fSFrançois Tigeot }
237e3adcf8fSFrançois Tigeot 
238e3adcf8fSFrançois Tigeot /* Workaround for i830 bug where pipe a must be enable to change control regs */
239e3adcf8fSFrançois Tigeot static int
240e3adcf8fSFrançois Tigeot i830_activate_pipe_a(struct drm_device *dev)
241e3adcf8fSFrançois Tigeot {
242e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
243e3adcf8fSFrançois Tigeot 	struct intel_crtc *crtc;
244e3adcf8fSFrançois Tigeot 	struct drm_crtc_helper_funcs *crtc_funcs;
245e3adcf8fSFrançois Tigeot 	struct drm_display_mode vesa_640x480 = {
246e3adcf8fSFrançois Tigeot 		DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
247e3adcf8fSFrançois Tigeot 			 752, 800, 0, 480, 489, 492, 525, 0,
248e3adcf8fSFrançois Tigeot 			 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
249e3adcf8fSFrançois Tigeot 	}, *mode;
250e3adcf8fSFrançois Tigeot 
251e3adcf8fSFrançois Tigeot 	crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]);
252e3adcf8fSFrançois Tigeot 	if (crtc->dpms_mode == DRM_MODE_DPMS_ON)
253e3adcf8fSFrançois Tigeot 		return 0;
254e3adcf8fSFrançois Tigeot 
255e3adcf8fSFrançois Tigeot 	/* most i8xx have pipe a forced on, so don't trust dpms mode */
256e3adcf8fSFrançois Tigeot 	if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE)
257e3adcf8fSFrançois Tigeot 		return 0;
258e3adcf8fSFrançois Tigeot 
259e3adcf8fSFrançois Tigeot 	crtc_funcs = crtc->base.helper_private;
260e3adcf8fSFrançois Tigeot 	if (crtc_funcs->dpms == NULL)
261e3adcf8fSFrançois Tigeot 		return 0;
262e3adcf8fSFrançois Tigeot 
263e3adcf8fSFrançois Tigeot 	DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n");
264e3adcf8fSFrançois Tigeot 
265e3adcf8fSFrançois Tigeot 	mode = drm_mode_duplicate(dev, &vesa_640x480);
266e3adcf8fSFrançois Tigeot 	drm_mode_set_crtcinfo(mode, 0);
267e3adcf8fSFrançois Tigeot 	if (!drm_crtc_helper_set_mode(&crtc->base, mode,
268e3adcf8fSFrançois Tigeot 				       crtc->base.x, crtc->base.y,
269e3adcf8fSFrançois Tigeot 				       crtc->base.fb))
270e3adcf8fSFrançois Tigeot 		return 0;
271e3adcf8fSFrançois Tigeot 
272e3adcf8fSFrançois Tigeot 	crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON);
273e3adcf8fSFrançois Tigeot 	return 1;
274e3adcf8fSFrançois Tigeot }
275e3adcf8fSFrançois Tigeot 
276e3adcf8fSFrançois Tigeot static void
277e3adcf8fSFrançois Tigeot i830_deactivate_pipe_a(struct drm_device *dev)
278e3adcf8fSFrançois Tigeot {
279e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
280e3adcf8fSFrançois Tigeot 	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
281e3adcf8fSFrançois Tigeot 	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
282e3adcf8fSFrançois Tigeot 
283e3adcf8fSFrançois Tigeot 	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
284e3adcf8fSFrançois Tigeot }
285e3adcf8fSFrançois Tigeot 
286e3adcf8fSFrançois Tigeot /* overlay needs to be disable in OCMD reg */
287e3adcf8fSFrançois Tigeot static int intel_overlay_on(struct intel_overlay *overlay)
288e3adcf8fSFrançois Tigeot {
289e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
290e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
291e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_request *request;
292e3adcf8fSFrançois Tigeot 	int pipe_a_quirk = 0;
293e3adcf8fSFrançois Tigeot 	int ret;
294e3adcf8fSFrançois Tigeot 
295e3adcf8fSFrançois Tigeot 	KASSERT(!overlay->active, ("Overlay is active"));
296e3adcf8fSFrançois Tigeot 	overlay->active = 1;
297e3adcf8fSFrançois Tigeot 
298e3adcf8fSFrançois Tigeot 	if (IS_I830(dev)) {
299e3adcf8fSFrançois Tigeot 		pipe_a_quirk = i830_activate_pipe_a(dev);
300e3adcf8fSFrançois Tigeot 		if (pipe_a_quirk < 0)
301e3adcf8fSFrançois Tigeot 			return pipe_a_quirk;
302e3adcf8fSFrançois Tigeot 	}
303e3adcf8fSFrançois Tigeot 
304e3adcf8fSFrançois Tigeot 	request = kmalloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
305e3adcf8fSFrançois Tigeot 
306e3adcf8fSFrançois Tigeot 	ret = BEGIN_LP_RING(4);
307e3adcf8fSFrançois Tigeot 	if (ret) {
308e3adcf8fSFrançois Tigeot 		drm_free(request, DRM_I915_GEM);
309e3adcf8fSFrançois Tigeot 		goto out;
310e3adcf8fSFrançois Tigeot 	}
311e3adcf8fSFrançois Tigeot 
312e3adcf8fSFrançois Tigeot 	OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
313e3adcf8fSFrançois Tigeot 	OUT_RING(overlay->flip_addr | OFC_UPDATE);
314e3adcf8fSFrançois Tigeot 	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
315e3adcf8fSFrançois Tigeot 	OUT_RING(MI_NOOP);
316e3adcf8fSFrançois Tigeot 	ADVANCE_LP_RING();
317e3adcf8fSFrançois Tigeot 
318e3adcf8fSFrançois Tigeot 	ret = intel_overlay_do_wait_request(overlay, request, NULL);
319e3adcf8fSFrançois Tigeot out:
320e3adcf8fSFrançois Tigeot 	if (pipe_a_quirk)
321e3adcf8fSFrançois Tigeot 		i830_deactivate_pipe_a(dev);
322e3adcf8fSFrançois Tigeot 
323e3adcf8fSFrançois Tigeot 	return ret;
324e3adcf8fSFrançois Tigeot }
325e3adcf8fSFrançois Tigeot 
326e3adcf8fSFrançois Tigeot /* overlay needs to be enabled in OCMD reg */
327e3adcf8fSFrançois Tigeot static int intel_overlay_continue(struct intel_overlay *overlay,
328e3adcf8fSFrançois Tigeot 				  bool load_polyphase_filter)
329e3adcf8fSFrançois Tigeot {
330e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
331e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
332e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_request *request;
333e3adcf8fSFrançois Tigeot 	u32 flip_addr = overlay->flip_addr;
334e3adcf8fSFrançois Tigeot 	u32 tmp;
335e3adcf8fSFrançois Tigeot 	int ret;
336e3adcf8fSFrançois Tigeot 
337e3adcf8fSFrançois Tigeot 	KASSERT(overlay->active, ("Overlay not active"));
338e3adcf8fSFrançois Tigeot 
339e3adcf8fSFrançois Tigeot 	request = kmalloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
340e3adcf8fSFrançois Tigeot 
341e3adcf8fSFrançois Tigeot 	if (load_polyphase_filter)
342e3adcf8fSFrançois Tigeot 		flip_addr |= OFC_UPDATE;
343e3adcf8fSFrançois Tigeot 
344e3adcf8fSFrançois Tigeot 	/* check for underruns */
345e3adcf8fSFrançois Tigeot 	tmp = I915_READ(DOVSTA);
346e3adcf8fSFrançois Tigeot 	if (tmp & (1 << 17))
347e3adcf8fSFrançois Tigeot 		DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
348e3adcf8fSFrançois Tigeot 
349e3adcf8fSFrançois Tigeot 	ret = BEGIN_LP_RING(2);
350e3adcf8fSFrançois Tigeot 	if (ret) {
351e3adcf8fSFrançois Tigeot 		drm_free(request, DRM_I915_GEM);
352e3adcf8fSFrançois Tigeot 		return ret;
353e3adcf8fSFrançois Tigeot 	}
354e3adcf8fSFrançois Tigeot 	OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
355e3adcf8fSFrançois Tigeot 	OUT_RING(flip_addr);
356e3adcf8fSFrançois Tigeot 	ADVANCE_LP_RING();
357e3adcf8fSFrançois Tigeot 
358e3adcf8fSFrançois Tigeot 	ret = i915_add_request(LP_RING(dev_priv), NULL, request);
359e3adcf8fSFrançois Tigeot 	if (ret) {
360e3adcf8fSFrançois Tigeot 		drm_free(request, DRM_I915_GEM);
361e3adcf8fSFrançois Tigeot 		return ret;
362e3adcf8fSFrançois Tigeot 	}
363e3adcf8fSFrançois Tigeot 
364e3adcf8fSFrançois Tigeot 	overlay->last_flip_req = request->seqno;
365e3adcf8fSFrançois Tigeot 	return 0;
366e3adcf8fSFrançois Tigeot }
367e3adcf8fSFrançois Tigeot 
368e3adcf8fSFrançois Tigeot static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
369e3adcf8fSFrançois Tigeot {
370e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *obj = overlay->old_vid_bo;
371e3adcf8fSFrançois Tigeot 
372e3adcf8fSFrançois Tigeot 	i915_gem_object_unpin(obj);
373e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&obj->base);
374e3adcf8fSFrançois Tigeot 
375e3adcf8fSFrançois Tigeot 	overlay->old_vid_bo = NULL;
376e3adcf8fSFrançois Tigeot }
377e3adcf8fSFrançois Tigeot 
378e3adcf8fSFrançois Tigeot static void intel_overlay_off_tail(struct intel_overlay *overlay)
379e3adcf8fSFrançois Tigeot {
380e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *obj = overlay->vid_bo;
381e3adcf8fSFrançois Tigeot 
382e3adcf8fSFrançois Tigeot 	/* never have the overlay hw on without showing a frame */
383e3adcf8fSFrançois Tigeot 	KASSERT(overlay->vid_bo != NULL, ("No vid_bo"));
384e3adcf8fSFrançois Tigeot 
385e3adcf8fSFrançois Tigeot 	i915_gem_object_unpin(obj);
386e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&obj->base);
387e3adcf8fSFrançois Tigeot 	overlay->vid_bo = NULL;
388e3adcf8fSFrançois Tigeot 
389e3adcf8fSFrançois Tigeot 	overlay->crtc->overlay = NULL;
390e3adcf8fSFrançois Tigeot 	overlay->crtc = NULL;
391e3adcf8fSFrançois Tigeot 	overlay->active = 0;
392e3adcf8fSFrançois Tigeot }
393e3adcf8fSFrançois Tigeot 
394e3adcf8fSFrançois Tigeot /* overlay needs to be disabled in OCMD reg */
395e3adcf8fSFrançois Tigeot static int intel_overlay_off(struct intel_overlay *overlay)
396e3adcf8fSFrançois Tigeot {
397e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
398e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
399e3adcf8fSFrançois Tigeot 	u32 flip_addr = overlay->flip_addr;
400e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_request *request;
401e3adcf8fSFrançois Tigeot 	int ret;
402e3adcf8fSFrançois Tigeot 
403e3adcf8fSFrançois Tigeot 	KASSERT(overlay->active, ("Overlay is not active"));
404e3adcf8fSFrançois Tigeot 
405e3adcf8fSFrançois Tigeot 	request = kmalloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
406e3adcf8fSFrançois Tigeot 
407e3adcf8fSFrançois Tigeot 	/* According to intel docs the overlay hw may hang (when switching
408e3adcf8fSFrançois Tigeot 	 * off) without loading the filter coeffs. It is however unclear whether
409e3adcf8fSFrançois Tigeot 	 * this applies to the disabling of the overlay or to the switching off
410e3adcf8fSFrançois Tigeot 	 * of the hw. Do it in both cases */
411e3adcf8fSFrançois Tigeot 	flip_addr |= OFC_UPDATE;
412e3adcf8fSFrançois Tigeot 
413e3adcf8fSFrançois Tigeot 	ret = BEGIN_LP_RING(6);
414e3adcf8fSFrançois Tigeot 	if (ret) {
415e3adcf8fSFrançois Tigeot 		drm_free(request, DRM_I915_GEM);
416e3adcf8fSFrançois Tigeot 		return ret;
417e3adcf8fSFrançois Tigeot 	}
418e3adcf8fSFrançois Tigeot 	/* wait for overlay to go idle */
419e3adcf8fSFrançois Tigeot 	OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
420e3adcf8fSFrançois Tigeot 	OUT_RING(flip_addr);
421e3adcf8fSFrançois Tigeot 	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
422e3adcf8fSFrançois Tigeot 	/* turn overlay off */
423e3adcf8fSFrançois Tigeot 	OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
424e3adcf8fSFrançois Tigeot 	OUT_RING(flip_addr);
425e3adcf8fSFrançois Tigeot 	OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
426e3adcf8fSFrançois Tigeot 	ADVANCE_LP_RING();
427e3adcf8fSFrançois Tigeot 
428e3adcf8fSFrançois Tigeot 	return intel_overlay_do_wait_request(overlay, request,
429e3adcf8fSFrançois Tigeot 					     intel_overlay_off_tail);
430e3adcf8fSFrançois Tigeot }
431e3adcf8fSFrançois Tigeot 
432e3adcf8fSFrançois Tigeot /* recover from an interruption due to a signal
433e3adcf8fSFrançois Tigeot  * We have to be careful not to repeat work forever an make forward progess. */
434e3adcf8fSFrançois Tigeot static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
435e3adcf8fSFrançois Tigeot {
436e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
437e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
438e3adcf8fSFrançois Tigeot 	int ret;
439e3adcf8fSFrançois Tigeot 
440e3adcf8fSFrançois Tigeot 	if (overlay->last_flip_req == 0)
441e3adcf8fSFrançois Tigeot 		return 0;
442e3adcf8fSFrançois Tigeot 
443e3adcf8fSFrançois Tigeot 	ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req,
444e3adcf8fSFrançois Tigeot 				true);
445e3adcf8fSFrançois Tigeot 	if (ret)
446e3adcf8fSFrançois Tigeot 		return ret;
447e3adcf8fSFrançois Tigeot 
448e3adcf8fSFrançois Tigeot 	if (overlay->flip_tail)
449e3adcf8fSFrançois Tigeot 		overlay->flip_tail(overlay);
450e3adcf8fSFrançois Tigeot 
451e3adcf8fSFrançois Tigeot 	overlay->last_flip_req = 0;
452e3adcf8fSFrançois Tigeot 	return 0;
453e3adcf8fSFrançois Tigeot }
454e3adcf8fSFrançois Tigeot 
455e3adcf8fSFrançois Tigeot /* Wait for pending overlay flip and release old frame.
456e3adcf8fSFrançois Tigeot  * Needs to be called before the overlay register are changed
457e3adcf8fSFrançois Tigeot  * via intel_overlay_(un)map_regs
458e3adcf8fSFrançois Tigeot  */
459e3adcf8fSFrançois Tigeot static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
460e3adcf8fSFrançois Tigeot {
461e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
462e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
463e3adcf8fSFrançois Tigeot 	int ret;
464e3adcf8fSFrançois Tigeot 
465e3adcf8fSFrançois Tigeot 	/* Only wait if there is actually an old frame to release to
466e3adcf8fSFrançois Tigeot 	 * guarantee forward progress.
467e3adcf8fSFrançois Tigeot 	 */
468e3adcf8fSFrançois Tigeot 	if (!overlay->old_vid_bo)
469e3adcf8fSFrançois Tigeot 		return 0;
470e3adcf8fSFrançois Tigeot 
471e3adcf8fSFrançois Tigeot 	if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) {
472e3adcf8fSFrançois Tigeot 		struct drm_i915_gem_request *request;
473e3adcf8fSFrançois Tigeot 
474e3adcf8fSFrançois Tigeot 		/* synchronous slowpath */
475e3adcf8fSFrançois Tigeot 		request = kmalloc(sizeof(*request), DRM_I915_GEM, M_WAITOK | M_ZERO);
476e3adcf8fSFrançois Tigeot 
477e3adcf8fSFrançois Tigeot 		ret = BEGIN_LP_RING(2);
478e3adcf8fSFrançois Tigeot 		if (ret) {
479e3adcf8fSFrançois Tigeot 			drm_free(request, DRM_I915_GEM);
480e3adcf8fSFrançois Tigeot 			return ret;
481e3adcf8fSFrançois Tigeot 		}
482e3adcf8fSFrançois Tigeot 
483e3adcf8fSFrançois Tigeot 		OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
484e3adcf8fSFrançois Tigeot 		OUT_RING(MI_NOOP);
485e3adcf8fSFrançois Tigeot 		ADVANCE_LP_RING();
486e3adcf8fSFrançois Tigeot 
487e3adcf8fSFrançois Tigeot 		ret = intel_overlay_do_wait_request(overlay, request,
488e3adcf8fSFrançois Tigeot 						    intel_overlay_release_old_vid_tail);
489e3adcf8fSFrançois Tigeot 		if (ret)
490e3adcf8fSFrançois Tigeot 			return ret;
491e3adcf8fSFrançois Tigeot 	}
492e3adcf8fSFrançois Tigeot 
493e3adcf8fSFrançois Tigeot 	intel_overlay_release_old_vid_tail(overlay);
494e3adcf8fSFrançois Tigeot 	return 0;
495e3adcf8fSFrançois Tigeot }
496e3adcf8fSFrançois Tigeot 
497e3adcf8fSFrançois Tigeot struct put_image_params {
498e3adcf8fSFrançois Tigeot 	int format;
499e3adcf8fSFrançois Tigeot 	short dst_x;
500e3adcf8fSFrançois Tigeot 	short dst_y;
501e3adcf8fSFrançois Tigeot 	short dst_w;
502e3adcf8fSFrançois Tigeot 	short dst_h;
503e3adcf8fSFrançois Tigeot 	short src_w;
504e3adcf8fSFrançois Tigeot 	short src_scan_h;
505e3adcf8fSFrançois Tigeot 	short src_scan_w;
506e3adcf8fSFrançois Tigeot 	short src_h;
507e3adcf8fSFrançois Tigeot 	short stride_Y;
508e3adcf8fSFrançois Tigeot 	short stride_UV;
509e3adcf8fSFrançois Tigeot 	int offset_Y;
510e3adcf8fSFrançois Tigeot 	int offset_U;
511e3adcf8fSFrançois Tigeot 	int offset_V;
512e3adcf8fSFrançois Tigeot };
513e3adcf8fSFrançois Tigeot 
514e3adcf8fSFrançois Tigeot static int packed_depth_bytes(u32 format)
515e3adcf8fSFrançois Tigeot {
516e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
517e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
518e3adcf8fSFrançois Tigeot 		return 4;
519e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
520e3adcf8fSFrançois Tigeot 		/* return 6; not implemented */
521e3adcf8fSFrançois Tigeot 	default:
522e3adcf8fSFrançois Tigeot 		return -EINVAL;
523e3adcf8fSFrançois Tigeot 	}
524e3adcf8fSFrançois Tigeot }
525e3adcf8fSFrançois Tigeot 
526e3adcf8fSFrançois Tigeot static int packed_width_bytes(u32 format, short width)
527e3adcf8fSFrançois Tigeot {
528e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
529e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
530e3adcf8fSFrançois Tigeot 		return width << 1;
531e3adcf8fSFrançois Tigeot 	default:
532e3adcf8fSFrançois Tigeot 		return -EINVAL;
533e3adcf8fSFrançois Tigeot 	}
534e3adcf8fSFrançois Tigeot }
535e3adcf8fSFrançois Tigeot 
536e3adcf8fSFrançois Tigeot static int uv_hsubsampling(u32 format)
537e3adcf8fSFrançois Tigeot {
538e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
539e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
540e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV420:
541e3adcf8fSFrançois Tigeot 		return 2;
542e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
543e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV410:
544e3adcf8fSFrançois Tigeot 		return 4;
545e3adcf8fSFrançois Tigeot 	default:
546e3adcf8fSFrançois Tigeot 		return -EINVAL;
547e3adcf8fSFrançois Tigeot 	}
548e3adcf8fSFrançois Tigeot }
549e3adcf8fSFrançois Tigeot 
550e3adcf8fSFrançois Tigeot static int uv_vsubsampling(u32 format)
551e3adcf8fSFrançois Tigeot {
552e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
553e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV420:
554e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV410:
555e3adcf8fSFrançois Tigeot 		return 2;
556e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
557e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
558e3adcf8fSFrançois Tigeot 		return 1;
559e3adcf8fSFrançois Tigeot 	default:
560e3adcf8fSFrançois Tigeot 		return -EINVAL;
561e3adcf8fSFrançois Tigeot 	}
562e3adcf8fSFrançois Tigeot }
563e3adcf8fSFrançois Tigeot 
564e3adcf8fSFrançois Tigeot static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
565e3adcf8fSFrançois Tigeot {
566e3adcf8fSFrançois Tigeot 	u32 mask, shift, ret;
567e3adcf8fSFrançois Tigeot 	if (IS_GEN2(dev)) {
568e3adcf8fSFrançois Tigeot 		mask = 0x1f;
569e3adcf8fSFrançois Tigeot 		shift = 5;
570e3adcf8fSFrançois Tigeot 	} else {
571e3adcf8fSFrançois Tigeot 		mask = 0x3f;
572e3adcf8fSFrançois Tigeot 		shift = 6;
573e3adcf8fSFrançois Tigeot 	}
574e3adcf8fSFrançois Tigeot 	ret = ((offset + width + mask) >> shift) - (offset >> shift);
575e3adcf8fSFrançois Tigeot 	if (!IS_GEN2(dev))
576e3adcf8fSFrançois Tigeot 		ret <<= 1;
577e3adcf8fSFrançois Tigeot 	ret -= 1;
578e3adcf8fSFrançois Tigeot 	return ret << 2;
579e3adcf8fSFrançois Tigeot }
580e3adcf8fSFrançois Tigeot 
581e3adcf8fSFrançois Tigeot static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
582e3adcf8fSFrançois Tigeot 	0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
583e3adcf8fSFrançois Tigeot 	0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
584e3adcf8fSFrançois Tigeot 	0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
585e3adcf8fSFrançois Tigeot 	0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
586e3adcf8fSFrançois Tigeot 	0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
587e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
588e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
589e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
590e3adcf8fSFrançois Tigeot 	0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
591e3adcf8fSFrançois Tigeot 	0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
592e3adcf8fSFrançois Tigeot 	0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
593e3adcf8fSFrançois Tigeot 	0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
594e3adcf8fSFrançois Tigeot 	0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
595e3adcf8fSFrançois Tigeot 	0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
596e3adcf8fSFrançois Tigeot 	0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
597e3adcf8fSFrançois Tigeot 	0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
598e3adcf8fSFrançois Tigeot 	0xb000, 0x3000, 0x0800, 0x3000, 0xb000
599e3adcf8fSFrançois Tigeot };
600e3adcf8fSFrançois Tigeot 
601e3adcf8fSFrançois Tigeot static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
602e3adcf8fSFrançois Tigeot 	0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
603e3adcf8fSFrançois Tigeot 	0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
604e3adcf8fSFrançois Tigeot 	0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
605e3adcf8fSFrançois Tigeot 	0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
606e3adcf8fSFrançois Tigeot 	0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
607e3adcf8fSFrançois Tigeot 	0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
608e3adcf8fSFrançois Tigeot 	0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
609e3adcf8fSFrançois Tigeot 	0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
610e3adcf8fSFrançois Tigeot 	0x3000, 0x0800, 0x3000
611e3adcf8fSFrançois Tigeot };
612e3adcf8fSFrançois Tigeot 
613e3adcf8fSFrançois Tigeot static void update_polyphase_filter(struct overlay_registers *regs)
614e3adcf8fSFrançois Tigeot {
615e3adcf8fSFrançois Tigeot 	memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
616e3adcf8fSFrançois Tigeot 	memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs));
617e3adcf8fSFrançois Tigeot }
618e3adcf8fSFrançois Tigeot 
619e3adcf8fSFrançois Tigeot static bool update_scaling_factors(struct intel_overlay *overlay,
620e3adcf8fSFrançois Tigeot 				   struct overlay_registers *regs,
621e3adcf8fSFrançois Tigeot 				   struct put_image_params *params)
622e3adcf8fSFrançois Tigeot {
623e3adcf8fSFrançois Tigeot 	/* fixed point with a 12 bit shift */
624e3adcf8fSFrançois Tigeot 	u32 xscale, yscale, xscale_UV, yscale_UV;
625e3adcf8fSFrançois Tigeot #define FP_SHIFT 12
626e3adcf8fSFrançois Tigeot #define FRACT_MASK 0xfff
627e3adcf8fSFrançois Tigeot 	bool scale_changed = false;
628e3adcf8fSFrançois Tigeot 	int uv_hscale = uv_hsubsampling(params->format);
629e3adcf8fSFrançois Tigeot 	int uv_vscale = uv_vsubsampling(params->format);
630e3adcf8fSFrançois Tigeot 
631e3adcf8fSFrançois Tigeot 	if (params->dst_w > 1)
632e3adcf8fSFrançois Tigeot 		xscale = ((params->src_scan_w - 1) << FP_SHIFT)
633e3adcf8fSFrançois Tigeot 			/(params->dst_w);
634e3adcf8fSFrançois Tigeot 	else
635e3adcf8fSFrançois Tigeot 		xscale = 1 << FP_SHIFT;
636e3adcf8fSFrançois Tigeot 
637e3adcf8fSFrançois Tigeot 	if (params->dst_h > 1)
638e3adcf8fSFrançois Tigeot 		yscale = ((params->src_scan_h - 1) << FP_SHIFT)
639e3adcf8fSFrançois Tigeot 			/(params->dst_h);
640e3adcf8fSFrançois Tigeot 	else
641e3adcf8fSFrançois Tigeot 		yscale = 1 << FP_SHIFT;
642e3adcf8fSFrançois Tigeot 
643e3adcf8fSFrançois Tigeot 	/*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
644e3adcf8fSFrançois Tigeot 	xscale_UV = xscale/uv_hscale;
645e3adcf8fSFrançois Tigeot 	yscale_UV = yscale/uv_vscale;
646e3adcf8fSFrançois Tigeot 	/* make the Y scale to UV scale ratio an exact multiply */
647e3adcf8fSFrançois Tigeot 	xscale = xscale_UV * uv_hscale;
648e3adcf8fSFrançois Tigeot 	yscale = yscale_UV * uv_vscale;
649e3adcf8fSFrançois Tigeot 	/*} else {
650e3adcf8fSFrançois Tigeot 	  xscale_UV = 0;
651e3adcf8fSFrançois Tigeot 	  yscale_UV = 0;
652e3adcf8fSFrançois Tigeot 	  }*/
653e3adcf8fSFrançois Tigeot 
654e3adcf8fSFrançois Tigeot 	if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
655e3adcf8fSFrançois Tigeot 		scale_changed = true;
656e3adcf8fSFrançois Tigeot 	overlay->old_xscale = xscale;
657e3adcf8fSFrançois Tigeot 	overlay->old_yscale = yscale;
658e3adcf8fSFrançois Tigeot 
659e3adcf8fSFrançois Tigeot 	regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) |
660e3adcf8fSFrançois Tigeot 			   ((xscale >> FP_SHIFT)  << 16) |
661e3adcf8fSFrançois Tigeot 			   ((xscale & FRACT_MASK) << 3));
662e3adcf8fSFrançois Tigeot 
663e3adcf8fSFrançois Tigeot 	regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) |
664e3adcf8fSFrançois Tigeot 			 ((xscale_UV >> FP_SHIFT)  << 16) |
665e3adcf8fSFrançois Tigeot 			 ((xscale_UV & FRACT_MASK) << 3));
666e3adcf8fSFrançois Tigeot 
667e3adcf8fSFrançois Tigeot 	regs->UVSCALEV = ((((yscale    >> FP_SHIFT) << 16) |
668e3adcf8fSFrançois Tigeot 			   ((yscale_UV >> FP_SHIFT) << 0)));
669e3adcf8fSFrançois Tigeot 
670e3adcf8fSFrançois Tigeot 	if (scale_changed)
671e3adcf8fSFrançois Tigeot 		update_polyphase_filter(regs);
672e3adcf8fSFrançois Tigeot 
673e3adcf8fSFrançois Tigeot 	return scale_changed;
674e3adcf8fSFrançois Tigeot }
675e3adcf8fSFrançois Tigeot 
676e3adcf8fSFrançois Tigeot static void update_colorkey(struct intel_overlay *overlay,
677e3adcf8fSFrançois Tigeot 			    struct overlay_registers *regs)
678e3adcf8fSFrançois Tigeot {
679e3adcf8fSFrançois Tigeot 	u32 key = overlay->color_key;
680e3adcf8fSFrançois Tigeot 
681e3adcf8fSFrançois Tigeot 	switch (overlay->crtc->base.fb->bits_per_pixel) {
682e3adcf8fSFrançois Tigeot 	case 8:
683e3adcf8fSFrançois Tigeot 		regs->DCLRKV = 0;
684e3adcf8fSFrançois Tigeot 		regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
685e3adcf8fSFrançois Tigeot 		break;
686e3adcf8fSFrançois Tigeot 
687e3adcf8fSFrançois Tigeot 	case 16:
688e3adcf8fSFrançois Tigeot 		if (overlay->crtc->base.fb->depth == 15) {
689e3adcf8fSFrançois Tigeot 			regs->DCLRKV = RGB15_TO_COLORKEY(key);
690e3adcf8fSFrançois Tigeot 			regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
691e3adcf8fSFrançois Tigeot 		} else {
692e3adcf8fSFrançois Tigeot 			regs->DCLRKV = RGB16_TO_COLORKEY(key);
693e3adcf8fSFrançois Tigeot 			regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
694e3adcf8fSFrançois Tigeot 		}
695e3adcf8fSFrançois Tigeot 		break;
696e3adcf8fSFrançois Tigeot 
697e3adcf8fSFrançois Tigeot 	case 24:
698e3adcf8fSFrançois Tigeot 	case 32:
699e3adcf8fSFrançois Tigeot 		regs->DCLRKV = key;
700e3adcf8fSFrançois Tigeot 		regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
701e3adcf8fSFrançois Tigeot 		break;
702e3adcf8fSFrançois Tigeot 	}
703e3adcf8fSFrançois Tigeot }
704e3adcf8fSFrançois Tigeot 
705e3adcf8fSFrançois Tigeot static u32 overlay_cmd_reg(struct put_image_params *params)
706e3adcf8fSFrançois Tigeot {
707e3adcf8fSFrançois Tigeot 	u32 cmd = OCMD_ENABLE | OCMD_BUF_TYPE_FRAME | OCMD_BUFFER0;
708e3adcf8fSFrançois Tigeot 
709e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PLANAR) {
710e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_DEPTH_MASK) {
711e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV422:
712e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_422_PLANAR;
713e3adcf8fSFrançois Tigeot 			break;
714e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV420:
715e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_420_PLANAR;
716e3adcf8fSFrançois Tigeot 			break;
717e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV411:
718e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV410:
719e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_410_PLANAR;
720e3adcf8fSFrançois Tigeot 			break;
721e3adcf8fSFrançois Tigeot 		}
722e3adcf8fSFrançois Tigeot 	} else { /* YUV packed */
723e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_DEPTH_MASK) {
724e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV422:
725e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_422_PACKED;
726e3adcf8fSFrançois Tigeot 			break;
727e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV411:
728e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_411_PACKED;
729e3adcf8fSFrançois Tigeot 			break;
730e3adcf8fSFrançois Tigeot 		}
731e3adcf8fSFrançois Tigeot 
732e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_SWAP_MASK) {
733e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_NO_SWAP:
734e3adcf8fSFrançois Tigeot 			break;
735e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_UV_SWAP:
736e3adcf8fSFrançois Tigeot 			cmd |= OCMD_UV_SWAP;
737e3adcf8fSFrançois Tigeot 			break;
738e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_Y_SWAP:
739e3adcf8fSFrançois Tigeot 			cmd |= OCMD_Y_SWAP;
740e3adcf8fSFrançois Tigeot 			break;
741e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_Y_AND_UV_SWAP:
742e3adcf8fSFrançois Tigeot 			cmd |= OCMD_Y_AND_UV_SWAP;
743e3adcf8fSFrançois Tigeot 			break;
744e3adcf8fSFrançois Tigeot 		}
745e3adcf8fSFrançois Tigeot 	}
746e3adcf8fSFrançois Tigeot 
747e3adcf8fSFrançois Tigeot 	return cmd;
748e3adcf8fSFrançois Tigeot }
749e3adcf8fSFrançois Tigeot 
750e3adcf8fSFrançois Tigeot static u32
751e3adcf8fSFrançois Tigeot max_u32(u32 a, u32 b)
752e3adcf8fSFrançois Tigeot {
753e3adcf8fSFrançois Tigeot 
754e3adcf8fSFrançois Tigeot 	return (a > b ? a : b);
755e3adcf8fSFrançois Tigeot }
756e3adcf8fSFrançois Tigeot 
757e3adcf8fSFrançois Tigeot static int intel_overlay_do_put_image(struct intel_overlay *overlay,
758e3adcf8fSFrançois Tigeot 				      struct drm_i915_gem_object *new_bo,
759e3adcf8fSFrançois Tigeot 				      struct put_image_params *params)
760e3adcf8fSFrançois Tigeot {
761e3adcf8fSFrançois Tigeot 	int ret, tmp_width;
762e3adcf8fSFrançois Tigeot 	struct overlay_registers *regs;
763e3adcf8fSFrançois Tigeot 	bool scale_changed = false;
764e3adcf8fSFrançois Tigeot 
765e3adcf8fSFrançois Tigeot 	KASSERT(overlay != NULL, ("No overlay ?"));
766e3adcf8fSFrançois Tigeot 	DRM_LOCK_ASSERT(overlay->dev);
767e3adcf8fSFrançois Tigeot 	DRM_MODE_CONFIG_ASSERT_LOCKED(overlay->dev);
768e3adcf8fSFrançois Tigeot 
769e3adcf8fSFrançois Tigeot 	ret = intel_overlay_release_old_vid(overlay);
770e3adcf8fSFrançois Tigeot 	if (ret != 0)
771e3adcf8fSFrançois Tigeot 		return ret;
772e3adcf8fSFrançois Tigeot 
773e3adcf8fSFrançois Tigeot 	ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL);
774e3adcf8fSFrançois Tigeot 	if (ret != 0)
775e3adcf8fSFrançois Tigeot 		goto out_unpin;
776e3adcf8fSFrançois Tigeot 
777e3adcf8fSFrançois Tigeot 	ret = i915_gem_object_put_fence(new_bo);
778e3adcf8fSFrançois Tigeot 	if (ret)
779e3adcf8fSFrançois Tigeot 		goto out_unpin;
780e3adcf8fSFrançois Tigeot 
781e3adcf8fSFrançois Tigeot 	if (!overlay->active) {
782e3adcf8fSFrançois Tigeot 		regs = intel_overlay_map_regs(overlay);
783e3adcf8fSFrançois Tigeot 		if (!regs) {
784e3adcf8fSFrançois Tigeot 			ret = -ENOMEM;
785e3adcf8fSFrançois Tigeot 			goto out_unpin;
786e3adcf8fSFrançois Tigeot 		}
787e3adcf8fSFrançois Tigeot 		regs->OCONFIG = OCONF_CC_OUT_8BIT;
788e3adcf8fSFrançois Tigeot 		if (IS_GEN4(overlay->dev))
789e3adcf8fSFrançois Tigeot 			regs->OCONFIG |= OCONF_CSC_MODE_BT709;
790e3adcf8fSFrançois Tigeot 		regs->OCONFIG |= overlay->crtc->pipe == 0 ?
791e3adcf8fSFrançois Tigeot 			OCONF_PIPE_A : OCONF_PIPE_B;
792e3adcf8fSFrançois Tigeot 		intel_overlay_unmap_regs(overlay, regs);
793e3adcf8fSFrançois Tigeot 
794e3adcf8fSFrançois Tigeot 		ret = intel_overlay_on(overlay);
795e3adcf8fSFrançois Tigeot 		if (ret != 0)
796e3adcf8fSFrançois Tigeot 			goto out_unpin;
797e3adcf8fSFrançois Tigeot 	}
798e3adcf8fSFrançois Tigeot 
799e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
800e3adcf8fSFrançois Tigeot 	if (!regs) {
801e3adcf8fSFrançois Tigeot 		ret = -ENOMEM;
802e3adcf8fSFrançois Tigeot 		goto out_unpin;
803e3adcf8fSFrançois Tigeot 	}
804e3adcf8fSFrançois Tigeot 
805e3adcf8fSFrançois Tigeot 	regs->DWINPOS = (params->dst_y << 16) | params->dst_x;
806e3adcf8fSFrançois Tigeot 	regs->DWINSZ = (params->dst_h << 16) | params->dst_w;
807e3adcf8fSFrançois Tigeot 
808e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PACKED)
809e3adcf8fSFrançois Tigeot 		tmp_width = packed_width_bytes(params->format, params->src_w);
810e3adcf8fSFrançois Tigeot 	else
811e3adcf8fSFrançois Tigeot 		tmp_width = params->src_w;
812e3adcf8fSFrançois Tigeot 
813e3adcf8fSFrançois Tigeot 	regs->SWIDTH = params->src_w;
814e3adcf8fSFrançois Tigeot 	regs->SWIDTHSW = calc_swidthsw(overlay->dev,
815e3adcf8fSFrançois Tigeot 				       params->offset_Y, tmp_width);
816e3adcf8fSFrançois Tigeot 	regs->SHEIGHT = params->src_h;
817e3adcf8fSFrançois Tigeot 	regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y;
818e3adcf8fSFrançois Tigeot 	regs->OSTRIDE = params->stride_Y;
819e3adcf8fSFrançois Tigeot 
820e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PLANAR) {
821e3adcf8fSFrançois Tigeot 		int uv_hscale = uv_hsubsampling(params->format);
822e3adcf8fSFrançois Tigeot 		int uv_vscale = uv_vsubsampling(params->format);
823e3adcf8fSFrançois Tigeot 		u32 tmp_U, tmp_V;
824e3adcf8fSFrançois Tigeot 		regs->SWIDTH |= (params->src_w/uv_hscale) << 16;
825e3adcf8fSFrançois Tigeot 		tmp_U = calc_swidthsw(overlay->dev, params->offset_U,
826e3adcf8fSFrançois Tigeot 				      params->src_w/uv_hscale);
827e3adcf8fSFrançois Tigeot 		tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
828e3adcf8fSFrançois Tigeot 				      params->src_w/uv_hscale);
829e3adcf8fSFrançois Tigeot 		regs->SWIDTHSW |= max_u32(tmp_U, tmp_V) << 16;
830e3adcf8fSFrançois Tigeot 		regs->SHEIGHT |= (params->src_h/uv_vscale) << 16;
831e3adcf8fSFrançois Tigeot 		regs->OBUF_0U = new_bo->gtt_offset + params->offset_U;
832e3adcf8fSFrançois Tigeot 		regs->OBUF_0V = new_bo->gtt_offset + params->offset_V;
833e3adcf8fSFrançois Tigeot 		regs->OSTRIDE |= params->stride_UV << 16;
834e3adcf8fSFrançois Tigeot 	}
835e3adcf8fSFrançois Tigeot 
836e3adcf8fSFrançois Tigeot 	scale_changed = update_scaling_factors(overlay, regs, params);
837e3adcf8fSFrançois Tigeot 
838e3adcf8fSFrançois Tigeot 	update_colorkey(overlay, regs);
839e3adcf8fSFrançois Tigeot 
840e3adcf8fSFrançois Tigeot 	regs->OCMD = overlay_cmd_reg(params);
841e3adcf8fSFrançois Tigeot 
842e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
843e3adcf8fSFrançois Tigeot 
844e3adcf8fSFrançois Tigeot 	ret = intel_overlay_continue(overlay, scale_changed);
845e3adcf8fSFrançois Tigeot 	if (ret)
846e3adcf8fSFrançois Tigeot 		goto out_unpin;
847e3adcf8fSFrançois Tigeot 
848e3adcf8fSFrançois Tigeot 	overlay->old_vid_bo = overlay->vid_bo;
849e3adcf8fSFrançois Tigeot 	overlay->vid_bo = new_bo;
850e3adcf8fSFrançois Tigeot 
851e3adcf8fSFrançois Tigeot 	return 0;
852e3adcf8fSFrançois Tigeot 
853e3adcf8fSFrançois Tigeot out_unpin:
854e3adcf8fSFrançois Tigeot 	i915_gem_object_unpin(new_bo);
855e3adcf8fSFrançois Tigeot 	return ret;
856e3adcf8fSFrançois Tigeot }
857e3adcf8fSFrançois Tigeot 
858e3adcf8fSFrançois Tigeot int intel_overlay_switch_off(struct intel_overlay *overlay)
859e3adcf8fSFrançois Tigeot {
860e3adcf8fSFrançois Tigeot 	struct overlay_registers *regs;
861e3adcf8fSFrançois Tigeot 	int ret;
862e3adcf8fSFrançois Tigeot 
863e3adcf8fSFrançois Tigeot 	DRM_LOCK_ASSERT(overlay->dev);
864e3adcf8fSFrançois Tigeot 	DRM_MODE_CONFIG_ASSERT_LOCKED(overlay->dev);
865e3adcf8fSFrançois Tigeot 
866e3adcf8fSFrançois Tigeot 	ret = intel_overlay_recover_from_interrupt(overlay);
867e3adcf8fSFrançois Tigeot 	if (ret != 0)
868e3adcf8fSFrançois Tigeot 		return ret;
869e3adcf8fSFrançois Tigeot 
870e3adcf8fSFrançois Tigeot 	if (!overlay->active)
871e3adcf8fSFrançois Tigeot 		return 0;
872e3adcf8fSFrançois Tigeot 
873e3adcf8fSFrançois Tigeot 	ret = intel_overlay_release_old_vid(overlay);
874e3adcf8fSFrançois Tigeot 	if (ret != 0)
875e3adcf8fSFrançois Tigeot 		return ret;
876e3adcf8fSFrançois Tigeot 
877e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
878e3adcf8fSFrançois Tigeot 	regs->OCMD = 0;
879e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
880e3adcf8fSFrançois Tigeot 
881e3adcf8fSFrançois Tigeot 	ret = intel_overlay_off(overlay);
882e3adcf8fSFrançois Tigeot 	if (ret != 0)
883e3adcf8fSFrançois Tigeot 		return ret;
884e3adcf8fSFrançois Tigeot 
885e3adcf8fSFrançois Tigeot 	intel_overlay_off_tail(overlay);
886e3adcf8fSFrançois Tigeot 	return 0;
887e3adcf8fSFrançois Tigeot }
888e3adcf8fSFrançois Tigeot 
889e3adcf8fSFrançois Tigeot static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
890e3adcf8fSFrançois Tigeot 					  struct intel_crtc *crtc)
891e3adcf8fSFrançois Tigeot {
892e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = overlay->dev->dev_private;
893e3adcf8fSFrançois Tigeot 
894e3adcf8fSFrançois Tigeot 	if (!crtc->active)
895e3adcf8fSFrançois Tigeot 		return -EINVAL;
896e3adcf8fSFrançois Tigeot 
897e3adcf8fSFrançois Tigeot 	/* can't use the overlay with double wide pipe */
898e3adcf8fSFrançois Tigeot 	if (INTEL_INFO(overlay->dev)->gen < 4 &&
899e3adcf8fSFrançois Tigeot 	    (I915_READ(PIPECONF(crtc->pipe)) & (PIPECONF_DOUBLE_WIDE | PIPECONF_ENABLE)) != PIPECONF_ENABLE)
900e3adcf8fSFrançois Tigeot 		return -EINVAL;
901e3adcf8fSFrançois Tigeot 
902e3adcf8fSFrançois Tigeot 	return 0;
903e3adcf8fSFrançois Tigeot }
904e3adcf8fSFrançois Tigeot 
905e3adcf8fSFrançois Tigeot static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
906e3adcf8fSFrançois Tigeot {
907e3adcf8fSFrançois Tigeot 	struct drm_device *dev = overlay->dev;
908e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
909e3adcf8fSFrançois Tigeot 	u32 pfit_control = I915_READ(PFIT_CONTROL);
910e3adcf8fSFrançois Tigeot 	u32 ratio;
911e3adcf8fSFrançois Tigeot 
912e3adcf8fSFrançois Tigeot 	/* XXX: This is not the same logic as in the xorg driver, but more in
913e3adcf8fSFrançois Tigeot 	 * line with the intel documentation for the i965
914e3adcf8fSFrançois Tigeot 	 */
915e3adcf8fSFrançois Tigeot 	if (INTEL_INFO(dev)->gen >= 4) {
916e3adcf8fSFrançois Tigeot 		/* on i965 use the PGM reg to read out the autoscaler values */
917e3adcf8fSFrançois Tigeot 		ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965;
918e3adcf8fSFrançois Tigeot 	} else {
919e3adcf8fSFrançois Tigeot 		if (pfit_control & VERT_AUTO_SCALE)
920e3adcf8fSFrançois Tigeot 			ratio = I915_READ(PFIT_AUTO_RATIOS);
921e3adcf8fSFrançois Tigeot 		else
922e3adcf8fSFrançois Tigeot 			ratio = I915_READ(PFIT_PGM_RATIOS);
923e3adcf8fSFrançois Tigeot 		ratio >>= PFIT_VERT_SCALE_SHIFT;
924e3adcf8fSFrançois Tigeot 	}
925e3adcf8fSFrançois Tigeot 
926e3adcf8fSFrançois Tigeot 	overlay->pfit_vscale_ratio = ratio;
927e3adcf8fSFrançois Tigeot }
928e3adcf8fSFrançois Tigeot 
929e3adcf8fSFrançois Tigeot static int check_overlay_dst(struct intel_overlay *overlay,
930e3adcf8fSFrançois Tigeot 			     struct drm_intel_overlay_put_image *rec)
931e3adcf8fSFrançois Tigeot {
932e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode = &overlay->crtc->base.mode;
933e3adcf8fSFrançois Tigeot 
934e3adcf8fSFrançois Tigeot 	if (rec->dst_x < mode->hdisplay &&
935e3adcf8fSFrançois Tigeot 	    rec->dst_x + rec->dst_width <= mode->hdisplay &&
936e3adcf8fSFrançois Tigeot 	    rec->dst_y < mode->vdisplay &&
937e3adcf8fSFrançois Tigeot 	    rec->dst_y + rec->dst_height <= mode->vdisplay)
938e3adcf8fSFrançois Tigeot 		return 0;
939e3adcf8fSFrançois Tigeot 	else
940e3adcf8fSFrançois Tigeot 		return -EINVAL;
941e3adcf8fSFrançois Tigeot }
942e3adcf8fSFrançois Tigeot 
943e3adcf8fSFrançois Tigeot static int check_overlay_scaling(struct put_image_params *rec)
944e3adcf8fSFrançois Tigeot {
945e3adcf8fSFrançois Tigeot 	u32 tmp;
946e3adcf8fSFrançois Tigeot 
947e3adcf8fSFrançois Tigeot 	/* downscaling limit is 8.0 */
948e3adcf8fSFrançois Tigeot 	tmp = ((rec->src_scan_h << 16) / rec->dst_h) >> 16;
949e3adcf8fSFrançois Tigeot 	if (tmp > 7)
950e3adcf8fSFrançois Tigeot 		return -EINVAL;
951e3adcf8fSFrançois Tigeot 	tmp = ((rec->src_scan_w << 16) / rec->dst_w) >> 16;
952e3adcf8fSFrançois Tigeot 	if (tmp > 7)
953e3adcf8fSFrançois Tigeot 		return -EINVAL;
954e3adcf8fSFrançois Tigeot 
955e3adcf8fSFrançois Tigeot 	return 0;
956e3adcf8fSFrançois Tigeot }
957e3adcf8fSFrançois Tigeot 
958e3adcf8fSFrançois Tigeot static int check_overlay_src(struct drm_device *dev,
959e3adcf8fSFrançois Tigeot 			     struct drm_intel_overlay_put_image *rec,
960e3adcf8fSFrançois Tigeot 			     struct drm_i915_gem_object *new_bo)
961e3adcf8fSFrançois Tigeot {
962e3adcf8fSFrançois Tigeot 	int uv_hscale = uv_hsubsampling(rec->flags);
963e3adcf8fSFrançois Tigeot 	int uv_vscale = uv_vsubsampling(rec->flags);
964e3adcf8fSFrançois Tigeot 	u32 stride_mask;
965e3adcf8fSFrançois Tigeot 	int depth;
966e3adcf8fSFrançois Tigeot 	u32 tmp;
967e3adcf8fSFrançois Tigeot 
968e3adcf8fSFrançois Tigeot 	/* check src dimensions */
969e3adcf8fSFrançois Tigeot 	if (IS_845G(dev) || IS_I830(dev)) {
970e3adcf8fSFrançois Tigeot 		if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY ||
971e3adcf8fSFrançois Tigeot 		    rec->src_width  > IMAGE_MAX_WIDTH_LEGACY)
972e3adcf8fSFrançois Tigeot 			return -EINVAL;
973e3adcf8fSFrançois Tigeot 	} else {
974e3adcf8fSFrançois Tigeot 		if (rec->src_height > IMAGE_MAX_HEIGHT ||
975e3adcf8fSFrançois Tigeot 		    rec->src_width  > IMAGE_MAX_WIDTH)
976e3adcf8fSFrançois Tigeot 			return -EINVAL;
977e3adcf8fSFrançois Tigeot 	}
978e3adcf8fSFrançois Tigeot 
979e3adcf8fSFrançois Tigeot 	/* better safe than sorry, use 4 as the maximal subsampling ratio */
980e3adcf8fSFrançois Tigeot 	if (rec->src_height < N_VERT_Y_TAPS*4 ||
981e3adcf8fSFrançois Tigeot 	    rec->src_width  < N_HORIZ_Y_TAPS*4)
982e3adcf8fSFrançois Tigeot 		return -EINVAL;
983e3adcf8fSFrançois Tigeot 
984e3adcf8fSFrançois Tigeot 	/* check alignment constraints */
985e3adcf8fSFrançois Tigeot 	switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
986e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_RGB:
987e3adcf8fSFrançois Tigeot 		/* not implemented */
988e3adcf8fSFrançois Tigeot 		return -EINVAL;
989e3adcf8fSFrançois Tigeot 
990e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PACKED:
991e3adcf8fSFrançois Tigeot 		if (uv_vscale != 1)
992e3adcf8fSFrançois Tigeot 			return -EINVAL;
993e3adcf8fSFrançois Tigeot 
994e3adcf8fSFrançois Tigeot 		depth = packed_depth_bytes(rec->flags);
995e3adcf8fSFrançois Tigeot 		if (depth < 0)
996e3adcf8fSFrançois Tigeot 			return depth;
997e3adcf8fSFrançois Tigeot 
998e3adcf8fSFrançois Tigeot 		/* ignore UV planes */
999e3adcf8fSFrançois Tigeot 		rec->stride_UV = 0;
1000e3adcf8fSFrançois Tigeot 		rec->offset_U = 0;
1001e3adcf8fSFrançois Tigeot 		rec->offset_V = 0;
1002e3adcf8fSFrançois Tigeot 		/* check pixel alignment */
1003e3adcf8fSFrançois Tigeot 		if (rec->offset_Y % depth)
1004e3adcf8fSFrançois Tigeot 			return -EINVAL;
1005e3adcf8fSFrançois Tigeot 		break;
1006e3adcf8fSFrançois Tigeot 
1007e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PLANAR:
1008e3adcf8fSFrançois Tigeot 		if (uv_vscale < 0 || uv_hscale < 0)
1009e3adcf8fSFrançois Tigeot 			return -EINVAL;
1010e3adcf8fSFrançois Tigeot 		/* no offset restrictions for planar formats */
1011e3adcf8fSFrançois Tigeot 		break;
1012e3adcf8fSFrançois Tigeot 
1013e3adcf8fSFrançois Tigeot 	default:
1014e3adcf8fSFrançois Tigeot 		return -EINVAL;
1015e3adcf8fSFrançois Tigeot 	}
1016e3adcf8fSFrançois Tigeot 
1017e3adcf8fSFrançois Tigeot 	if (rec->src_width % uv_hscale)
1018e3adcf8fSFrançois Tigeot 		return -EINVAL;
1019e3adcf8fSFrançois Tigeot 
1020e3adcf8fSFrançois Tigeot 	/* stride checking */
1021e3adcf8fSFrançois Tigeot 	if (IS_I830(dev) || IS_845G(dev))
1022e3adcf8fSFrançois Tigeot 		stride_mask = 255;
1023e3adcf8fSFrançois Tigeot 	else
1024e3adcf8fSFrançois Tigeot 		stride_mask = 63;
1025e3adcf8fSFrançois Tigeot 
1026e3adcf8fSFrançois Tigeot 	if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
1027e3adcf8fSFrançois Tigeot 		return -EINVAL;
1028e3adcf8fSFrançois Tigeot 	if (IS_GEN4(dev) && rec->stride_Y < 512)
1029e3adcf8fSFrançois Tigeot 		return -EINVAL;
1030e3adcf8fSFrançois Tigeot 
1031e3adcf8fSFrançois Tigeot 	tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
1032e3adcf8fSFrançois Tigeot 		4096 : 8192;
1033e3adcf8fSFrançois Tigeot 	if (rec->stride_Y > tmp || rec->stride_UV > 2*1024)
1034e3adcf8fSFrançois Tigeot 		return -EINVAL;
1035e3adcf8fSFrançois Tigeot 
1036e3adcf8fSFrançois Tigeot 	/* check buffer dimensions */
1037e3adcf8fSFrançois Tigeot 	switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
1038e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_RGB:
1039e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PACKED:
1040e3adcf8fSFrançois Tigeot 		/* always 4 Y values per depth pixels */
1041e3adcf8fSFrançois Tigeot 		if (packed_width_bytes(rec->flags, rec->src_width) > rec->stride_Y)
1042e3adcf8fSFrançois Tigeot 			return -EINVAL;
1043e3adcf8fSFrançois Tigeot 
1044e3adcf8fSFrançois Tigeot 		tmp = rec->stride_Y*rec->src_height;
1045e3adcf8fSFrançois Tigeot 		if (rec->offset_Y + tmp > new_bo->base.size)
1046e3adcf8fSFrançois Tigeot 			return -EINVAL;
1047e3adcf8fSFrançois Tigeot 		break;
1048e3adcf8fSFrançois Tigeot 
1049e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PLANAR:
1050e3adcf8fSFrançois Tigeot 		if (rec->src_width > rec->stride_Y)
1051e3adcf8fSFrançois Tigeot 			return -EINVAL;
1052e3adcf8fSFrançois Tigeot 		if (rec->src_width/uv_hscale > rec->stride_UV)
1053e3adcf8fSFrançois Tigeot 			return -EINVAL;
1054e3adcf8fSFrançois Tigeot 
1055e3adcf8fSFrançois Tigeot 		tmp = rec->stride_Y * rec->src_height;
1056e3adcf8fSFrançois Tigeot 		if (rec->offset_Y + tmp > new_bo->base.size)
1057e3adcf8fSFrançois Tigeot 			return -EINVAL;
1058e3adcf8fSFrançois Tigeot 
1059e3adcf8fSFrançois Tigeot 		tmp = rec->stride_UV * (rec->src_height / uv_vscale);
1060e3adcf8fSFrançois Tigeot 		if (rec->offset_U + tmp > new_bo->base.size ||
1061e3adcf8fSFrançois Tigeot 		    rec->offset_V + tmp > new_bo->base.size)
1062e3adcf8fSFrançois Tigeot 			return -EINVAL;
1063e3adcf8fSFrançois Tigeot 		break;
1064e3adcf8fSFrançois Tigeot 	}
1065e3adcf8fSFrançois Tigeot 
1066e3adcf8fSFrançois Tigeot 	return 0;
1067e3adcf8fSFrançois Tigeot }
1068e3adcf8fSFrançois Tigeot 
1069e3adcf8fSFrançois Tigeot /**
1070e3adcf8fSFrançois Tigeot  * Return the pipe currently connected to the panel fitter,
1071e3adcf8fSFrançois Tigeot  * or -1 if the panel fitter is not present or not in use
1072e3adcf8fSFrançois Tigeot  */
1073e3adcf8fSFrançois Tigeot static int intel_panel_fitter_pipe(struct drm_device *dev)
1074e3adcf8fSFrançois Tigeot {
1075e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1076e3adcf8fSFrançois Tigeot 	u32  pfit_control;
1077e3adcf8fSFrançois Tigeot 
1078e3adcf8fSFrançois Tigeot 	/* i830 doesn't have a panel fitter */
1079e3adcf8fSFrançois Tigeot 	if (IS_I830(dev))
1080e3adcf8fSFrançois Tigeot 		return -1;
1081e3adcf8fSFrançois Tigeot 
1082e3adcf8fSFrançois Tigeot 	pfit_control = I915_READ(PFIT_CONTROL);
1083e3adcf8fSFrançois Tigeot 
1084e3adcf8fSFrançois Tigeot 	/* See if the panel fitter is in use */
1085e3adcf8fSFrançois Tigeot 	if ((pfit_control & PFIT_ENABLE) == 0)
1086e3adcf8fSFrançois Tigeot 		return -1;
1087e3adcf8fSFrançois Tigeot 
1088e3adcf8fSFrançois Tigeot 	/* 965 can place panel fitter on either pipe */
1089e3adcf8fSFrançois Tigeot 	if (IS_GEN4(dev))
1090e3adcf8fSFrançois Tigeot 		return (pfit_control >> 29) & 0x3;
1091e3adcf8fSFrançois Tigeot 
1092e3adcf8fSFrançois Tigeot 	/* older chips can only use pipe 1 */
1093e3adcf8fSFrançois Tigeot 	return 1;
1094e3adcf8fSFrançois Tigeot }
1095e3adcf8fSFrançois Tigeot 
1096e3adcf8fSFrançois Tigeot int intel_overlay_put_image(struct drm_device *dev, void *data,
1097e3adcf8fSFrançois Tigeot 			    struct drm_file *file_priv)
1098e3adcf8fSFrançois Tigeot {
1099e3adcf8fSFrançois Tigeot 	struct drm_intel_overlay_put_image *put_image_rec = data;
1100e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
1101e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
1102e3adcf8fSFrançois Tigeot 	struct drm_mode_object *drmmode_obj;
1103e3adcf8fSFrançois Tigeot 	struct intel_crtc *crtc;
1104e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *new_bo;
1105e3adcf8fSFrançois Tigeot 	struct put_image_params *params;
1106e3adcf8fSFrançois Tigeot 	int ret;
1107e3adcf8fSFrançois Tigeot 
1108e3adcf8fSFrançois Tigeot 	if (!dev_priv) {
1109e3adcf8fSFrançois Tigeot 		DRM_ERROR("called with no initialization\n");
1110e3adcf8fSFrançois Tigeot 		return -EINVAL;
1111e3adcf8fSFrançois Tigeot 	}
1112e3adcf8fSFrançois Tigeot 
1113e3adcf8fSFrançois Tigeot 	overlay = dev_priv->overlay;
1114e3adcf8fSFrançois Tigeot 	if (!overlay) {
1115e3adcf8fSFrançois Tigeot 		DRM_DEBUG("userspace bug: no overlay\n");
1116e3adcf8fSFrançois Tigeot 		return -ENODEV;
1117e3adcf8fSFrançois Tigeot 	}
1118e3adcf8fSFrançois Tigeot 
1119e3adcf8fSFrançois Tigeot 	if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
1120e3adcf8fSFrançois Tigeot 		lockmgr(&dev->mode_config.lock, LK_EXCLUSIVE);
1121e3adcf8fSFrançois Tigeot 		DRM_LOCK(dev);
1122e3adcf8fSFrançois Tigeot 
1123e3adcf8fSFrançois Tigeot 		ret = intel_overlay_switch_off(overlay);
1124e3adcf8fSFrançois Tigeot 
1125e3adcf8fSFrançois Tigeot 		DRM_UNLOCK(dev);
1126e3adcf8fSFrançois Tigeot 		lockmgr(&dev->mode_config.lock, LK_RELEASE);
1127e3adcf8fSFrançois Tigeot 
1128e3adcf8fSFrançois Tigeot 		return ret;
1129e3adcf8fSFrançois Tigeot 	}
1130e3adcf8fSFrançois Tigeot 
1131e3adcf8fSFrançois Tigeot 	params = kmalloc(sizeof(struct put_image_params), DRM_I915_GEM,
1132e3adcf8fSFrançois Tigeot 	    M_WAITOK | M_ZERO);
1133e3adcf8fSFrançois Tigeot 
1134e3adcf8fSFrançois Tigeot 	drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
1135e3adcf8fSFrançois Tigeot 					   DRM_MODE_OBJECT_CRTC);
1136e3adcf8fSFrançois Tigeot 	if (!drmmode_obj) {
1137e3adcf8fSFrançois Tigeot 		ret = -ENOENT;
1138e3adcf8fSFrançois Tigeot 		goto out_free;
1139e3adcf8fSFrançois Tigeot 	}
1140e3adcf8fSFrançois Tigeot 	crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
1141e3adcf8fSFrançois Tigeot 
1142e3adcf8fSFrançois Tigeot 	new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv,
1143e3adcf8fSFrançois Tigeot 						   put_image_rec->bo_handle));
1144e3adcf8fSFrançois Tigeot 	if (&new_bo->base == NULL) {
1145e3adcf8fSFrançois Tigeot 		ret = -ENOENT;
1146e3adcf8fSFrançois Tigeot 		goto out_free;
1147e3adcf8fSFrançois Tigeot 	}
1148e3adcf8fSFrançois Tigeot 
1149e3adcf8fSFrançois Tigeot 	lockmgr(&dev->mode_config.lock, LK_EXCLUSIVE);
1150e3adcf8fSFrançois Tigeot 	DRM_LOCK(dev);
1151e3adcf8fSFrançois Tigeot 
1152e3adcf8fSFrançois Tigeot 	if (new_bo->tiling_mode) {
1153e3adcf8fSFrançois Tigeot 		DRM_ERROR("buffer used for overlay image can not be tiled\n");
1154e3adcf8fSFrançois Tigeot 		ret = -EINVAL;
1155e3adcf8fSFrançois Tigeot 		goto out_unlock;
1156e3adcf8fSFrançois Tigeot 	}
1157e3adcf8fSFrançois Tigeot 
1158e3adcf8fSFrançois Tigeot 	ret = intel_overlay_recover_from_interrupt(overlay);
1159e3adcf8fSFrançois Tigeot 	if (ret != 0)
1160e3adcf8fSFrançois Tigeot 		goto out_unlock;
1161e3adcf8fSFrançois Tigeot 
1162e3adcf8fSFrançois Tigeot 	if (overlay->crtc != crtc) {
1163e3adcf8fSFrançois Tigeot 		struct drm_display_mode *mode = &crtc->base.mode;
1164e3adcf8fSFrançois Tigeot 		ret = intel_overlay_switch_off(overlay);
1165e3adcf8fSFrançois Tigeot 		if (ret != 0)
1166e3adcf8fSFrançois Tigeot 			goto out_unlock;
1167e3adcf8fSFrançois Tigeot 
1168e3adcf8fSFrançois Tigeot 		ret = check_overlay_possible_on_crtc(overlay, crtc);
1169e3adcf8fSFrançois Tigeot 		if (ret != 0)
1170e3adcf8fSFrançois Tigeot 			goto out_unlock;
1171e3adcf8fSFrançois Tigeot 
1172e3adcf8fSFrançois Tigeot 		overlay->crtc = crtc;
1173e3adcf8fSFrançois Tigeot 		crtc->overlay = overlay;
1174e3adcf8fSFrançois Tigeot 
1175e3adcf8fSFrançois Tigeot 		/* line too wide, i.e. one-line-mode */
1176e3adcf8fSFrançois Tigeot 		if (mode->hdisplay > 1024 &&
1177e3adcf8fSFrançois Tigeot 		    intel_panel_fitter_pipe(dev) == crtc->pipe) {
1178e3adcf8fSFrançois Tigeot 			overlay->pfit_active = 1;
1179e3adcf8fSFrançois Tigeot 			update_pfit_vscale_ratio(overlay);
1180e3adcf8fSFrançois Tigeot 		} else
1181e3adcf8fSFrançois Tigeot 			overlay->pfit_active = 0;
1182e3adcf8fSFrançois Tigeot 	}
1183e3adcf8fSFrançois Tigeot 
1184e3adcf8fSFrançois Tigeot 	ret = check_overlay_dst(overlay, put_image_rec);
1185e3adcf8fSFrançois Tigeot 	if (ret != 0)
1186e3adcf8fSFrançois Tigeot 		goto out_unlock;
1187e3adcf8fSFrançois Tigeot 
1188e3adcf8fSFrançois Tigeot 	if (overlay->pfit_active) {
1189e3adcf8fSFrançois Tigeot 		params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
1190e3adcf8fSFrançois Tigeot 				 overlay->pfit_vscale_ratio);
1191e3adcf8fSFrançois Tigeot 		/* shifting right rounds downwards, so add 1 */
1192e3adcf8fSFrançois Tigeot 		params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
1193e3adcf8fSFrançois Tigeot 				 overlay->pfit_vscale_ratio) + 1;
1194e3adcf8fSFrançois Tigeot 	} else {
1195e3adcf8fSFrançois Tigeot 		params->dst_y = put_image_rec->dst_y;
1196e3adcf8fSFrançois Tigeot 		params->dst_h = put_image_rec->dst_height;
1197e3adcf8fSFrançois Tigeot 	}
1198e3adcf8fSFrançois Tigeot 	params->dst_x = put_image_rec->dst_x;
1199e3adcf8fSFrançois Tigeot 	params->dst_w = put_image_rec->dst_width;
1200e3adcf8fSFrançois Tigeot 
1201e3adcf8fSFrançois Tigeot 	params->src_w = put_image_rec->src_width;
1202e3adcf8fSFrançois Tigeot 	params->src_h = put_image_rec->src_height;
1203e3adcf8fSFrançois Tigeot 	params->src_scan_w = put_image_rec->src_scan_width;
1204e3adcf8fSFrançois Tigeot 	params->src_scan_h = put_image_rec->src_scan_height;
1205e3adcf8fSFrançois Tigeot 	if (params->src_scan_h > params->src_h ||
1206e3adcf8fSFrançois Tigeot 	    params->src_scan_w > params->src_w) {
1207e3adcf8fSFrançois Tigeot 		ret = -EINVAL;
1208e3adcf8fSFrançois Tigeot 		goto out_unlock;
1209e3adcf8fSFrançois Tigeot 	}
1210e3adcf8fSFrançois Tigeot 
1211e3adcf8fSFrançois Tigeot 	ret = check_overlay_src(dev, put_image_rec, new_bo);
1212e3adcf8fSFrançois Tigeot 	if (ret != 0)
1213e3adcf8fSFrançois Tigeot 		goto out_unlock;
1214e3adcf8fSFrançois Tigeot 	params->format = put_image_rec->flags & ~I915_OVERLAY_FLAGS_MASK;
1215e3adcf8fSFrançois Tigeot 	params->stride_Y = put_image_rec->stride_Y;
1216e3adcf8fSFrançois Tigeot 	params->stride_UV = put_image_rec->stride_UV;
1217e3adcf8fSFrançois Tigeot 	params->offset_Y = put_image_rec->offset_Y;
1218e3adcf8fSFrançois Tigeot 	params->offset_U = put_image_rec->offset_U;
1219e3adcf8fSFrançois Tigeot 	params->offset_V = put_image_rec->offset_V;
1220e3adcf8fSFrançois Tigeot 
1221e3adcf8fSFrançois Tigeot 	/* Check scaling after src size to prevent a divide-by-zero. */
1222e3adcf8fSFrançois Tigeot 	ret = check_overlay_scaling(params);
1223e3adcf8fSFrançois Tigeot 	if (ret != 0)
1224e3adcf8fSFrançois Tigeot 		goto out_unlock;
1225e3adcf8fSFrançois Tigeot 
1226e3adcf8fSFrançois Tigeot 	ret = intel_overlay_do_put_image(overlay, new_bo, params);
1227e3adcf8fSFrançois Tigeot 	if (ret != 0)
1228e3adcf8fSFrançois Tigeot 		goto out_unlock;
1229e3adcf8fSFrançois Tigeot 
1230e3adcf8fSFrançois Tigeot 	DRM_UNLOCK(dev);
1231e3adcf8fSFrançois Tigeot 	lockmgr(&dev->mode_config.lock, LK_RELEASE);
1232e3adcf8fSFrançois Tigeot 
1233e3adcf8fSFrançois Tigeot 	drm_free(params, DRM_I915_GEM);
1234e3adcf8fSFrançois Tigeot 
1235e3adcf8fSFrançois Tigeot 	return 0;
1236e3adcf8fSFrançois Tigeot 
1237e3adcf8fSFrançois Tigeot out_unlock:
1238e3adcf8fSFrançois Tigeot 	DRM_UNLOCK(dev);
1239e3adcf8fSFrançois Tigeot 	lockmgr(&dev->mode_config.lock, LK_RELEASE);
1240e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference_unlocked(&new_bo->base);
1241e3adcf8fSFrançois Tigeot out_free:
1242e3adcf8fSFrançois Tigeot 	drm_free(params, DRM_I915_GEM);
1243e3adcf8fSFrançois Tigeot 
1244e3adcf8fSFrançois Tigeot 	return ret;
1245e3adcf8fSFrançois Tigeot }
1246e3adcf8fSFrançois Tigeot 
1247e3adcf8fSFrançois Tigeot static void update_reg_attrs(struct intel_overlay *overlay,
1248e3adcf8fSFrançois Tigeot 			     struct overlay_registers *regs)
1249e3adcf8fSFrançois Tigeot {
1250e3adcf8fSFrançois Tigeot 	regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff);
1251e3adcf8fSFrançois Tigeot 	regs->OCLRC1 = overlay->saturation;
1252e3adcf8fSFrançois Tigeot }
1253e3adcf8fSFrançois Tigeot 
1254e3adcf8fSFrançois Tigeot static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
1255e3adcf8fSFrançois Tigeot {
1256e3adcf8fSFrançois Tigeot 	int i;
1257e3adcf8fSFrançois Tigeot 
1258e3adcf8fSFrançois Tigeot 	if (gamma1 & 0xff000000 || gamma2 & 0xff000000)
1259e3adcf8fSFrançois Tigeot 		return false;
1260e3adcf8fSFrançois Tigeot 
1261e3adcf8fSFrançois Tigeot 	for (i = 0; i < 3; i++) {
1262e3adcf8fSFrançois Tigeot 		if (((gamma1 >> i*8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
1263e3adcf8fSFrançois Tigeot 			return false;
1264e3adcf8fSFrançois Tigeot 	}
1265e3adcf8fSFrançois Tigeot 
1266e3adcf8fSFrançois Tigeot 	return true;
1267e3adcf8fSFrançois Tigeot }
1268e3adcf8fSFrançois Tigeot 
1269e3adcf8fSFrançois Tigeot static bool check_gamma5_errata(u32 gamma5)
1270e3adcf8fSFrançois Tigeot {
1271e3adcf8fSFrançois Tigeot 	int i;
1272e3adcf8fSFrançois Tigeot 
1273e3adcf8fSFrançois Tigeot 	for (i = 0; i < 3; i++) {
1274e3adcf8fSFrançois Tigeot 		if (((gamma5 >> i*8) & 0xff) == 0x80)
1275e3adcf8fSFrançois Tigeot 			return false;
1276e3adcf8fSFrançois Tigeot 	}
1277e3adcf8fSFrançois Tigeot 
1278e3adcf8fSFrançois Tigeot 	return true;
1279e3adcf8fSFrançois Tigeot }
1280e3adcf8fSFrançois Tigeot 
1281e3adcf8fSFrançois Tigeot static int check_gamma(struct drm_intel_overlay_attrs *attrs)
1282e3adcf8fSFrançois Tigeot {
1283e3adcf8fSFrançois Tigeot 	if (!check_gamma_bounds(0, attrs->gamma0) ||
1284e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma0, attrs->gamma1) ||
1285e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma1, attrs->gamma2) ||
1286e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma2, attrs->gamma3) ||
1287e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma3, attrs->gamma4) ||
1288e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma4, attrs->gamma5) ||
1289e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
1290e3adcf8fSFrançois Tigeot 		return -EINVAL;
1291e3adcf8fSFrançois Tigeot 
1292e3adcf8fSFrançois Tigeot 	if (!check_gamma5_errata(attrs->gamma5))
1293e3adcf8fSFrançois Tigeot 		return -EINVAL;
1294e3adcf8fSFrançois Tigeot 
1295e3adcf8fSFrançois Tigeot 	return 0;
1296e3adcf8fSFrançois Tigeot }
1297e3adcf8fSFrançois Tigeot 
1298e3adcf8fSFrançois Tigeot int intel_overlay_attrs(struct drm_device *dev, void *data,
1299e3adcf8fSFrançois Tigeot 			struct drm_file *file_priv)
1300e3adcf8fSFrançois Tigeot {
1301e3adcf8fSFrançois Tigeot 	struct drm_intel_overlay_attrs *attrs = data;
1302e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
1303e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
1304e3adcf8fSFrançois Tigeot 	struct overlay_registers *regs;
1305e3adcf8fSFrançois Tigeot 	int ret;
1306e3adcf8fSFrançois Tigeot 
1307e3adcf8fSFrançois Tigeot 	if (!dev_priv) {
1308e3adcf8fSFrançois Tigeot 		DRM_ERROR("called with no initialization\n");
1309e3adcf8fSFrançois Tigeot 		return -EINVAL;
1310e3adcf8fSFrançois Tigeot 	}
1311e3adcf8fSFrançois Tigeot 
1312e3adcf8fSFrançois Tigeot 	overlay = dev_priv->overlay;
1313e3adcf8fSFrançois Tigeot 	if (!overlay) {
1314e3adcf8fSFrançois Tigeot 		DRM_DEBUG("userspace bug: no overlay\n");
1315e3adcf8fSFrançois Tigeot 		return -ENODEV;
1316e3adcf8fSFrançois Tigeot 	}
1317e3adcf8fSFrançois Tigeot 
1318e3adcf8fSFrançois Tigeot 	lockmgr(&dev->mode_config.lock, LK_EXCLUSIVE);
1319e3adcf8fSFrançois Tigeot 	DRM_LOCK(dev);
1320e3adcf8fSFrançois Tigeot 
1321e3adcf8fSFrançois Tigeot 	ret = -EINVAL;
1322e3adcf8fSFrançois Tigeot 	if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
1323e3adcf8fSFrançois Tigeot 		attrs->color_key  = overlay->color_key;
1324e3adcf8fSFrançois Tigeot 		attrs->brightness = overlay->brightness;
1325e3adcf8fSFrançois Tigeot 		attrs->contrast   = overlay->contrast;
1326e3adcf8fSFrançois Tigeot 		attrs->saturation = overlay->saturation;
1327e3adcf8fSFrançois Tigeot 
1328e3adcf8fSFrançois Tigeot 		if (!IS_GEN2(dev)) {
1329e3adcf8fSFrançois Tigeot 			attrs->gamma0 = I915_READ(OGAMC0);
1330e3adcf8fSFrançois Tigeot 			attrs->gamma1 = I915_READ(OGAMC1);
1331e3adcf8fSFrançois Tigeot 			attrs->gamma2 = I915_READ(OGAMC2);
1332e3adcf8fSFrançois Tigeot 			attrs->gamma3 = I915_READ(OGAMC3);
1333e3adcf8fSFrançois Tigeot 			attrs->gamma4 = I915_READ(OGAMC4);
1334e3adcf8fSFrançois Tigeot 			attrs->gamma5 = I915_READ(OGAMC5);
1335e3adcf8fSFrançois Tigeot 		}
1336e3adcf8fSFrançois Tigeot 	} else {
1337e3adcf8fSFrançois Tigeot 		if (attrs->brightness < -128 || attrs->brightness > 127)
1338e3adcf8fSFrançois Tigeot 			goto out_unlock;
1339e3adcf8fSFrançois Tigeot 		if (attrs->contrast > 255)
1340e3adcf8fSFrançois Tigeot 			goto out_unlock;
1341e3adcf8fSFrançois Tigeot 		if (attrs->saturation > 1023)
1342e3adcf8fSFrançois Tigeot 			goto out_unlock;
1343e3adcf8fSFrançois Tigeot 
1344e3adcf8fSFrançois Tigeot 		overlay->color_key  = attrs->color_key;
1345e3adcf8fSFrançois Tigeot 		overlay->brightness = attrs->brightness;
1346e3adcf8fSFrançois Tigeot 		overlay->contrast   = attrs->contrast;
1347e3adcf8fSFrançois Tigeot 		overlay->saturation = attrs->saturation;
1348e3adcf8fSFrançois Tigeot 
1349e3adcf8fSFrançois Tigeot 		regs = intel_overlay_map_regs(overlay);
1350e3adcf8fSFrançois Tigeot 		if (!regs) {
1351e3adcf8fSFrançois Tigeot 			ret = -ENOMEM;
1352e3adcf8fSFrançois Tigeot 			goto out_unlock;
1353e3adcf8fSFrançois Tigeot 		}
1354e3adcf8fSFrançois Tigeot 
1355e3adcf8fSFrançois Tigeot 		update_reg_attrs(overlay, regs);
1356e3adcf8fSFrançois Tigeot 
1357e3adcf8fSFrançois Tigeot 		intel_overlay_unmap_regs(overlay, regs);
1358e3adcf8fSFrançois Tigeot 
1359e3adcf8fSFrançois Tigeot 		if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
1360e3adcf8fSFrançois Tigeot 			if (IS_GEN2(dev))
1361e3adcf8fSFrançois Tigeot 				goto out_unlock;
1362e3adcf8fSFrançois Tigeot 
1363e3adcf8fSFrançois Tigeot 			if (overlay->active) {
1364e3adcf8fSFrançois Tigeot 				ret = -EBUSY;
1365e3adcf8fSFrançois Tigeot 				goto out_unlock;
1366e3adcf8fSFrançois Tigeot 			}
1367e3adcf8fSFrançois Tigeot 
1368e3adcf8fSFrançois Tigeot 			ret = check_gamma(attrs);
1369e3adcf8fSFrançois Tigeot 			if (ret)
1370e3adcf8fSFrançois Tigeot 				goto out_unlock;
1371e3adcf8fSFrançois Tigeot 
1372e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC0, attrs->gamma0);
1373e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC1, attrs->gamma1);
1374e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC2, attrs->gamma2);
1375e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC3, attrs->gamma3);
1376e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC4, attrs->gamma4);
1377e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC5, attrs->gamma5);
1378e3adcf8fSFrançois Tigeot 		}
1379e3adcf8fSFrançois Tigeot 	}
1380e3adcf8fSFrançois Tigeot 
1381e3adcf8fSFrançois Tigeot 	ret = 0;
1382e3adcf8fSFrançois Tigeot out_unlock:
1383e3adcf8fSFrançois Tigeot 	DRM_UNLOCK(dev);
1384e3adcf8fSFrançois Tigeot 	lockmgr(&dev->mode_config.lock, LK_RELEASE);
1385e3adcf8fSFrançois Tigeot 
1386e3adcf8fSFrançois Tigeot 	return ret;
1387e3adcf8fSFrançois Tigeot }
1388e3adcf8fSFrançois Tigeot 
1389e3adcf8fSFrançois Tigeot void intel_setup_overlay(struct drm_device *dev)
1390e3adcf8fSFrançois Tigeot {
1391e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
1392e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
1393e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *reg_bo;
1394e3adcf8fSFrançois Tigeot 	struct overlay_registers *regs;
1395e3adcf8fSFrançois Tigeot 	int ret;
1396e3adcf8fSFrançois Tigeot 
1397e3adcf8fSFrançois Tigeot 	if (!HAS_OVERLAY(dev))
1398e3adcf8fSFrançois Tigeot 		return;
1399e3adcf8fSFrançois Tigeot 
1400e3adcf8fSFrançois Tigeot 	overlay = kmalloc(sizeof(struct intel_overlay), DRM_I915_GEM,
1401e3adcf8fSFrançois Tigeot 	    M_WAITOK | M_ZERO);
1402e3adcf8fSFrançois Tigeot 	DRM_LOCK(dev);
1403e3adcf8fSFrançois Tigeot 	if (dev_priv->overlay != NULL)
1404e3adcf8fSFrançois Tigeot 		goto out_free;
1405e3adcf8fSFrançois Tigeot 	overlay->dev = dev;
1406e3adcf8fSFrançois Tigeot 
1407e3adcf8fSFrançois Tigeot 	reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE);
1408e3adcf8fSFrançois Tigeot 	if (!reg_bo)
1409e3adcf8fSFrançois Tigeot 		goto out_free;
1410e3adcf8fSFrançois Tigeot 	overlay->reg_bo = reg_bo;
1411e3adcf8fSFrançois Tigeot 
1412e3adcf8fSFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(dev)) {
1413e3adcf8fSFrançois Tigeot 		ret = i915_gem_attach_phys_object(dev, reg_bo,
1414e3adcf8fSFrançois Tigeot 						  I915_GEM_PHYS_OVERLAY_REGS,
1415e3adcf8fSFrançois Tigeot 						  PAGE_SIZE);
1416e3adcf8fSFrançois Tigeot 		if (ret) {
1417e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to attach phys overlay regs\n");
1418e3adcf8fSFrançois Tigeot 			goto out_free_bo;
1419e3adcf8fSFrançois Tigeot 		}
1420e3adcf8fSFrançois Tigeot 		overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
1421e3adcf8fSFrançois Tigeot 	} else {
1422e3adcf8fSFrançois Tigeot 		ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true);
1423e3adcf8fSFrançois Tigeot 		if (ret) {
1424e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to pin overlay register bo\n");
1425e3adcf8fSFrançois Tigeot 			goto out_free_bo;
1426e3adcf8fSFrançois Tigeot 		}
1427e3adcf8fSFrançois Tigeot 		overlay->flip_addr = reg_bo->gtt_offset;
1428e3adcf8fSFrançois Tigeot 
1429e3adcf8fSFrançois Tigeot 		ret = i915_gem_object_set_to_gtt_domain(reg_bo, true);
1430e3adcf8fSFrançois Tigeot 		if (ret) {
1431e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to move overlay register bo into the GTT\n");
1432e3adcf8fSFrançois Tigeot 			goto out_unpin_bo;
1433e3adcf8fSFrançois Tigeot 		}
1434e3adcf8fSFrançois Tigeot 	}
1435e3adcf8fSFrançois Tigeot 
1436e3adcf8fSFrançois Tigeot 	/* init all values */
1437e3adcf8fSFrançois Tigeot 	overlay->color_key = 0x0101fe;
1438e3adcf8fSFrançois Tigeot 	overlay->brightness = -19;
1439e3adcf8fSFrançois Tigeot 	overlay->contrast = 75;
1440e3adcf8fSFrançois Tigeot 	overlay->saturation = 146;
1441e3adcf8fSFrançois Tigeot 
1442e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
1443e3adcf8fSFrançois Tigeot 	if (!regs)
1444e3adcf8fSFrançois Tigeot 		goto out_unpin_bo;
1445e3adcf8fSFrançois Tigeot 
1446e3adcf8fSFrançois Tigeot 	memset(regs, 0, sizeof(struct overlay_registers));
1447e3adcf8fSFrançois Tigeot 	update_polyphase_filter(regs);
1448e3adcf8fSFrançois Tigeot 	update_reg_attrs(overlay, regs);
1449e3adcf8fSFrançois Tigeot 
1450e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
1451e3adcf8fSFrançois Tigeot 
1452e3adcf8fSFrançois Tigeot 	dev_priv->overlay = overlay;
1453e3adcf8fSFrançois Tigeot 	DRM_INFO("initialized overlay support\n");
1454e3adcf8fSFrançois Tigeot 	DRM_UNLOCK(dev);
1455e3adcf8fSFrançois Tigeot 	return;
1456e3adcf8fSFrançois Tigeot 
1457e3adcf8fSFrançois Tigeot out_unpin_bo:
1458e3adcf8fSFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(dev))
1459e3adcf8fSFrançois Tigeot 		i915_gem_object_unpin(reg_bo);
1460e3adcf8fSFrançois Tigeot out_free_bo:
1461e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&reg_bo->base);
1462e3adcf8fSFrançois Tigeot out_free:
1463e3adcf8fSFrançois Tigeot 	DRM_UNLOCK(dev);
1464e3adcf8fSFrançois Tigeot 	drm_free(overlay, DRM_I915_GEM);
1465e3adcf8fSFrançois Tigeot 	return;
1466e3adcf8fSFrançois Tigeot }
1467e3adcf8fSFrançois Tigeot 
1468e3adcf8fSFrançois Tigeot void intel_cleanup_overlay(struct drm_device *dev)
1469e3adcf8fSFrançois Tigeot {
1470e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
1471e3adcf8fSFrançois Tigeot 
1472e3adcf8fSFrançois Tigeot 	if (!dev_priv->overlay)
1473e3adcf8fSFrançois Tigeot 		return;
1474e3adcf8fSFrançois Tigeot 
1475e3adcf8fSFrançois Tigeot 	/* The bo's should be free'd by the generic code already.
1476e3adcf8fSFrançois Tigeot 	 * Furthermore modesetting teardown happens beforehand so the
1477e3adcf8fSFrançois Tigeot 	 * hardware should be off already */
1478e3adcf8fSFrançois Tigeot 	KASSERT(!dev_priv->overlay->active, ("Overlay still active"));
1479e3adcf8fSFrançois Tigeot 
1480e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base);
1481e3adcf8fSFrançois Tigeot 	drm_free(dev_priv->overlay, DRM_I915_GEM);
1482e3adcf8fSFrançois Tigeot }
1483e3adcf8fSFrançois Tigeot 
1484e3adcf8fSFrançois Tigeot struct intel_overlay_error_state {
1485e3adcf8fSFrançois Tigeot 	struct overlay_registers regs;
1486e3adcf8fSFrançois Tigeot 	unsigned long base;
1487e3adcf8fSFrançois Tigeot 	u32 dovsta;
1488e3adcf8fSFrançois Tigeot 	u32 isr;
1489e3adcf8fSFrançois Tigeot };
1490e3adcf8fSFrançois Tigeot 
1491e3adcf8fSFrançois Tigeot struct intel_overlay_error_state *
1492e3adcf8fSFrançois Tigeot intel_overlay_capture_error_state(struct drm_device *dev)
1493e3adcf8fSFrançois Tigeot {
1494e3adcf8fSFrançois Tigeot 	drm_i915_private_t *dev_priv = dev->dev_private;
1495e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay = dev_priv->overlay;
1496e3adcf8fSFrançois Tigeot 	struct intel_overlay_error_state *error;
1497e3adcf8fSFrançois Tigeot 	struct overlay_registers __iomem *regs;
1498e3adcf8fSFrançois Tigeot 
1499e3adcf8fSFrançois Tigeot 	if (!overlay || !overlay->active)
1500e3adcf8fSFrançois Tigeot 		return NULL;
1501e3adcf8fSFrançois Tigeot 
1502e3adcf8fSFrançois Tigeot 	error = kmalloc(sizeof(*error), DRM_I915_GEM, M_NOWAIT);
1503e3adcf8fSFrançois Tigeot 	if (error == NULL)
1504e3adcf8fSFrançois Tigeot 		return NULL;
1505e3adcf8fSFrançois Tigeot 
1506e3adcf8fSFrançois Tigeot 	error->dovsta = I915_READ(DOVSTA);
1507e3adcf8fSFrançois Tigeot 	error->isr = I915_READ(ISR);
1508e3adcf8fSFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(overlay->dev))
1509e3adcf8fSFrançois Tigeot 		error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr;
1510e3adcf8fSFrançois Tigeot 	else
1511e3adcf8fSFrançois Tigeot 		error->base = (long) overlay->reg_bo->gtt_offset;
1512e3adcf8fSFrançois Tigeot 
1513e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
1514e3adcf8fSFrançois Tigeot 	if (!regs)
1515e3adcf8fSFrançois Tigeot 		goto err;
1516e3adcf8fSFrançois Tigeot 
1517e3adcf8fSFrançois Tigeot 	memcpy(&error->regs, regs, sizeof(struct overlay_registers));
1518e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
1519e3adcf8fSFrançois Tigeot 
1520e3adcf8fSFrançois Tigeot 	return (error);
1521e3adcf8fSFrançois Tigeot 
1522e3adcf8fSFrançois Tigeot err:
1523e3adcf8fSFrançois Tigeot 	drm_free(error, DRM_I915_GEM);
1524e3adcf8fSFrançois Tigeot 	return (NULL);
1525e3adcf8fSFrançois Tigeot }
1526e3adcf8fSFrançois Tigeot 
1527e3adcf8fSFrançois Tigeot void
1528e3adcf8fSFrançois Tigeot intel_overlay_print_error_state(struct sbuf *m,
1529e3adcf8fSFrançois Tigeot     struct intel_overlay_error_state *error)
1530e3adcf8fSFrançois Tigeot {
1531e3adcf8fSFrançois Tigeot 	sbuf_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
1532e3adcf8fSFrançois Tigeot 	    error->dovsta, error->isr);
1533e3adcf8fSFrançois Tigeot 	sbuf_printf(m, "  Register file at 0x%08lx:\n",
1534e3adcf8fSFrançois Tigeot 	    error->base);
1535e3adcf8fSFrançois Tigeot 
1536e3adcf8fSFrançois Tigeot #define P(x) sbuf_printf(m, "    " #x ":	0x%08x\n", error->regs.x)
1537e3adcf8fSFrançois Tigeot 	P(OBUF_0Y);
1538e3adcf8fSFrançois Tigeot 	P(OBUF_1Y);
1539e3adcf8fSFrançois Tigeot 	P(OBUF_0U);
1540e3adcf8fSFrançois Tigeot 	P(OBUF_0V);
1541e3adcf8fSFrançois Tigeot 	P(OBUF_1U);
1542e3adcf8fSFrançois Tigeot 	P(OBUF_1V);
1543e3adcf8fSFrançois Tigeot 	P(OSTRIDE);
1544e3adcf8fSFrançois Tigeot 	P(YRGB_VPH);
1545e3adcf8fSFrançois Tigeot 	P(UV_VPH);
1546e3adcf8fSFrançois Tigeot 	P(HORZ_PH);
1547e3adcf8fSFrançois Tigeot 	P(INIT_PHS);
1548e3adcf8fSFrançois Tigeot 	P(DWINPOS);
1549e3adcf8fSFrançois Tigeot 	P(DWINSZ);
1550e3adcf8fSFrançois Tigeot 	P(SWIDTH);
1551e3adcf8fSFrançois Tigeot 	P(SWIDTHSW);
1552e3adcf8fSFrançois Tigeot 	P(SHEIGHT);
1553e3adcf8fSFrançois Tigeot 	P(YRGBSCALE);
1554e3adcf8fSFrançois Tigeot 	P(UVSCALE);
1555e3adcf8fSFrançois Tigeot 	P(OCLRC0);
1556e3adcf8fSFrançois Tigeot 	P(OCLRC1);
1557e3adcf8fSFrançois Tigeot 	P(DCLRKV);
1558e3adcf8fSFrançois Tigeot 	P(DCLRKM);
1559e3adcf8fSFrançois Tigeot 	P(SCLRKVH);
1560e3adcf8fSFrançois Tigeot 	P(SCLRKVL);
1561e3adcf8fSFrançois Tigeot 	P(SCLRKEN);
1562e3adcf8fSFrançois Tigeot 	P(OCONFIG);
1563e3adcf8fSFrançois Tigeot 	P(OCMD);
1564e3adcf8fSFrançois Tigeot 	P(OSTART_0Y);
1565e3adcf8fSFrançois Tigeot 	P(OSTART_1Y);
1566e3adcf8fSFrançois Tigeot 	P(OSTART_0U);
1567e3adcf8fSFrançois Tigeot 	P(OSTART_0V);
1568e3adcf8fSFrançois Tigeot 	P(OSTART_1U);
1569e3adcf8fSFrançois Tigeot 	P(OSTART_1V);
1570e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0Y);
1571e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1Y);
1572e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0U);
1573e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0V);
1574e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1U);
1575e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1V);
1576e3adcf8fSFrançois Tigeot 	P(FASTHSCALE);
1577e3adcf8fSFrançois Tigeot 	P(UVSCALEV);
1578e3adcf8fSFrançois Tigeot #undef P
1579e3adcf8fSFrançois Tigeot }
1580