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