xref: /dflybsd-src/sys/dev/drm/i915/intel_overlay.c (revision bf0175970ba1c394f8039834a26ca6d163b23d0e)
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 {
1711487f786SFrançois Tigeot 	struct drm_i915_private *i915;
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;
17519c468b4SFrançois Tigeot 	bool active;
17619c468b4SFrançois Tigeot 	bool pfit_active;
177e3adcf8fSFrançois Tigeot 	u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
17819c468b4SFrançois Tigeot 	u32 color_key:24;
17919c468b4SFrançois Tigeot 	u32 color_key_enabled:1;
180e3adcf8fSFrançois Tigeot 	u32 brightness, contrast, saturation;
181e3adcf8fSFrançois Tigeot 	u32 old_xscale, old_yscale;
182e3adcf8fSFrançois Tigeot 	/* register access */
183e3adcf8fSFrançois Tigeot 	u32 flip_addr;
184e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *reg_bo;
185e3adcf8fSFrançois Tigeot 	/* flip handling */
1862c9916cdSFrançois Tigeot 	struct drm_i915_gem_request *last_flip_req;
187e3adcf8fSFrançois Tigeot 	void (*flip_tail)(struct intel_overlay *);
188e3adcf8fSFrançois Tigeot };
189e3adcf8fSFrançois Tigeot 
190e3440f96SFrançois Tigeot static struct overlay_registers __iomem *
191e3adcf8fSFrançois Tigeot intel_overlay_map_regs(struct intel_overlay *overlay)
192e3adcf8fSFrançois Tigeot {
1931487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
194e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
195e3adcf8fSFrançois Tigeot 
1961487f786SFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(dev_priv))
197ba55f2f5SFrançois Tigeot 		regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_handle->vaddr;
198e3440f96SFrançois Tigeot 	else
1991487f786SFrançois Tigeot 		regs = io_mapping_map_wc(dev_priv->ggtt.mappable,
2001487f786SFrançois Tigeot 					 overlay->flip_addr,
2011487f786SFrançois Tigeot 					 PAGE_SIZE);
202e3440f96SFrançois Tigeot 
203e3440f96SFrançois Tigeot 	return regs;
204e3adcf8fSFrançois Tigeot }
205e3adcf8fSFrançois Tigeot 
206e3adcf8fSFrançois Tigeot static void intel_overlay_unmap_regs(struct intel_overlay *overlay,
207e3440f96SFrançois Tigeot 				     struct overlay_registers __iomem *regs)
208e3adcf8fSFrançois Tigeot {
2091487f786SFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(overlay->i915))
210*bf017597SFrançois Tigeot 		io_mapping_unmap(regs);
211e3adcf8fSFrançois Tigeot }
212e3adcf8fSFrançois Tigeot 
213e3adcf8fSFrançois Tigeot static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
214a05eeebfSFrançois Tigeot 					 struct drm_i915_gem_request *req,
215e3adcf8fSFrançois Tigeot 					 void (*tail)(struct intel_overlay *))
216e3adcf8fSFrançois Tigeot {
217e3adcf8fSFrançois Tigeot 	int ret;
218e3adcf8fSFrançois Tigeot 
21919c468b4SFrançois Tigeot 	WARN_ON(overlay->last_flip_req);
220a05eeebfSFrançois Tigeot 	i915_gem_request_assign(&overlay->last_flip_req, req);
221a05eeebfSFrançois Tigeot 	i915_add_request(req);
222f192107fSFrançois Tigeot 
223e3adcf8fSFrançois Tigeot 	overlay->flip_tail = tail;
2242c9916cdSFrançois Tigeot 	ret = i915_wait_request(overlay->last_flip_req);
225e3adcf8fSFrançois Tigeot 	if (ret)
226e3adcf8fSFrançois Tigeot 		return ret;
227e3adcf8fSFrançois Tigeot 
2282c9916cdSFrançois Tigeot 	i915_gem_request_assign(&overlay->last_flip_req, NULL);
229e3adcf8fSFrançois Tigeot 	return 0;
230e3adcf8fSFrançois Tigeot }
231e3adcf8fSFrançois Tigeot 
232e3adcf8fSFrançois Tigeot /* overlay needs to be disable in OCMD reg */
233e3adcf8fSFrançois Tigeot static int intel_overlay_on(struct intel_overlay *overlay)
234e3adcf8fSFrançois Tigeot {
2351487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
2368621f407SFrançois Tigeot 	struct intel_engine_cs *engine = &dev_priv->engine[RCS];
237a05eeebfSFrançois Tigeot 	struct drm_i915_gem_request *req;
238e3adcf8fSFrançois Tigeot 	int ret;
239e3adcf8fSFrançois Tigeot 
24019c468b4SFrançois Tigeot 	WARN_ON(overlay->active);
2411487f786SFrançois Tigeot 	WARN_ON(IS_I830(dev_priv) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE));
242e3adcf8fSFrançois Tigeot 
2438621f407SFrançois Tigeot 	req = i915_gem_request_alloc(engine, NULL);
244c0e85e96SFrançois Tigeot 	if (IS_ERR(req))
245c0e85e96SFrançois Tigeot 		return PTR_ERR(req);
246f192107fSFrançois Tigeot 
247a05eeebfSFrançois Tigeot 	ret = intel_ring_begin(req, 4);
248a05eeebfSFrançois Tigeot 	if (ret) {
2498621f407SFrançois Tigeot 		i915_add_request_no_flush(req);
250a05eeebfSFrançois Tigeot 		return ret;
251a05eeebfSFrançois Tigeot 	}
252a05eeebfSFrançois Tigeot 
25319c468b4SFrançois Tigeot 	overlay->active = true;
25419c468b4SFrançois Tigeot 
2558621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_ON);
2568621f407SFrançois Tigeot 	intel_ring_emit(engine, overlay->flip_addr | OFC_UPDATE);
2578621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
2588621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_NOOP);
2598621f407SFrançois Tigeot 	intel_ring_advance(engine);
260f192107fSFrançois Tigeot 
261a05eeebfSFrançois Tigeot 	return intel_overlay_do_wait_request(overlay, req, NULL);
262e3adcf8fSFrançois Tigeot }
263e3adcf8fSFrançois Tigeot 
264e3adcf8fSFrançois Tigeot /* overlay needs to be enabled in OCMD reg */
265e3adcf8fSFrançois Tigeot static int intel_overlay_continue(struct intel_overlay *overlay,
266e3adcf8fSFrançois Tigeot 				  bool load_polyphase_filter)
267e3adcf8fSFrançois Tigeot {
2681487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
2698621f407SFrançois Tigeot 	struct intel_engine_cs *engine = &dev_priv->engine[RCS];
270a05eeebfSFrançois Tigeot 	struct drm_i915_gem_request *req;
271e3adcf8fSFrançois Tigeot 	u32 flip_addr = overlay->flip_addr;
272e3adcf8fSFrançois Tigeot 	u32 tmp;
273e3adcf8fSFrançois Tigeot 	int ret;
274e3adcf8fSFrançois Tigeot 
27519c468b4SFrançois Tigeot 	WARN_ON(!overlay->active);
276e3adcf8fSFrançois Tigeot 
277e3adcf8fSFrançois Tigeot 	if (load_polyphase_filter)
278e3adcf8fSFrançois Tigeot 		flip_addr |= OFC_UPDATE;
279e3adcf8fSFrançois Tigeot 
280e3adcf8fSFrançois Tigeot 	/* check for underruns */
281e3adcf8fSFrançois Tigeot 	tmp = I915_READ(DOVSTA);
282e3adcf8fSFrançois Tigeot 	if (tmp & (1 << 17))
283e3adcf8fSFrançois Tigeot 		DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
284e3adcf8fSFrançois Tigeot 
2858621f407SFrançois Tigeot 	req = i915_gem_request_alloc(engine, NULL);
286c0e85e96SFrançois Tigeot 	if (IS_ERR(req))
287c0e85e96SFrançois Tigeot 		return PTR_ERR(req);
288e3adcf8fSFrançois Tigeot 
289a05eeebfSFrançois Tigeot 	ret = intel_ring_begin(req, 2);
290a05eeebfSFrançois Tigeot 	if (ret) {
2918621f407SFrançois Tigeot 		i915_add_request_no_flush(req);
292a05eeebfSFrançois Tigeot 		return ret;
293a05eeebfSFrançois Tigeot 	}
294a05eeebfSFrançois Tigeot 
2958621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
2968621f407SFrançois Tigeot 	intel_ring_emit(engine, flip_addr);
2978621f407SFrançois Tigeot 	intel_ring_advance(engine);
298e3adcf8fSFrançois Tigeot 
2992c9916cdSFrançois Tigeot 	WARN_ON(overlay->last_flip_req);
300a05eeebfSFrançois Tigeot 	i915_gem_request_assign(&overlay->last_flip_req, req);
301a05eeebfSFrançois Tigeot 	i915_add_request(req);
302a05eeebfSFrançois Tigeot 
303a05eeebfSFrançois Tigeot 	return 0;
304e3adcf8fSFrançois Tigeot }
305e3adcf8fSFrançois Tigeot 
306e3adcf8fSFrançois Tigeot static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
307e3adcf8fSFrançois Tigeot {
308e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *obj = overlay->old_vid_bo;
309e3adcf8fSFrançois Tigeot 
310ba55f2f5SFrançois Tigeot 	i915_gem_object_ggtt_unpin(obj);
311e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&obj->base);
312e3adcf8fSFrançois Tigeot 
313e3adcf8fSFrançois Tigeot 	overlay->old_vid_bo = NULL;
314e3adcf8fSFrançois Tigeot }
315e3adcf8fSFrançois Tigeot 
316e3adcf8fSFrançois Tigeot static void intel_overlay_off_tail(struct intel_overlay *overlay)
317e3adcf8fSFrançois Tigeot {
318e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *obj = overlay->vid_bo;
319e3adcf8fSFrançois Tigeot 
320e3adcf8fSFrançois Tigeot 	/* never have the overlay hw on without showing a frame */
32119c468b4SFrançois Tigeot 	if (WARN_ON(!obj))
32219c468b4SFrançois Tigeot 		return;
323e3adcf8fSFrançois Tigeot 
324ba55f2f5SFrançois Tigeot 	i915_gem_object_ggtt_unpin(obj);
325e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&obj->base);
326e3adcf8fSFrançois Tigeot 	overlay->vid_bo = NULL;
327e3adcf8fSFrançois Tigeot 
328e3adcf8fSFrançois Tigeot 	overlay->crtc->overlay = NULL;
329e3adcf8fSFrançois Tigeot 	overlay->crtc = NULL;
33019c468b4SFrançois Tigeot 	overlay->active = false;
331e3adcf8fSFrançois Tigeot }
332e3adcf8fSFrançois Tigeot 
333e3adcf8fSFrançois Tigeot /* overlay needs to be disabled in OCMD reg */
334e3adcf8fSFrançois Tigeot static int intel_overlay_off(struct intel_overlay *overlay)
335e3adcf8fSFrançois Tigeot {
3361487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
3378621f407SFrançois Tigeot 	struct intel_engine_cs *engine = &dev_priv->engine[RCS];
338a05eeebfSFrançois Tigeot 	struct drm_i915_gem_request *req;
339e3adcf8fSFrançois Tigeot 	u32 flip_addr = overlay->flip_addr;
340e3adcf8fSFrançois Tigeot 	int ret;
341e3adcf8fSFrançois Tigeot 
34219c468b4SFrançois Tigeot 	WARN_ON(!overlay->active);
343e3adcf8fSFrançois Tigeot 
344e3adcf8fSFrançois Tigeot 	/* According to intel docs the overlay hw may hang (when switching
345e3adcf8fSFrançois Tigeot 	 * off) without loading the filter coeffs. It is however unclear whether
346e3adcf8fSFrançois Tigeot 	 * this applies to the disabling of the overlay or to the switching off
347e3adcf8fSFrançois Tigeot 	 * of the hw. Do it in both cases */
348e3adcf8fSFrançois Tigeot 	flip_addr |= OFC_UPDATE;
349e3adcf8fSFrançois Tigeot 
3508621f407SFrançois Tigeot 	req = i915_gem_request_alloc(engine, NULL);
351c0e85e96SFrançois Tigeot 	if (IS_ERR(req))
352c0e85e96SFrançois Tigeot 		return PTR_ERR(req);
353e3adcf8fSFrançois Tigeot 
354a05eeebfSFrançois Tigeot 	ret = intel_ring_begin(req, 6);
355a05eeebfSFrançois Tigeot 	if (ret) {
3568621f407SFrançois Tigeot 		i915_add_request_no_flush(req);
357a05eeebfSFrançois Tigeot 		return ret;
358a05eeebfSFrançois Tigeot 	}
359a05eeebfSFrançois Tigeot 
360f192107fSFrançois Tigeot 	/* wait for overlay to go idle */
3618621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
3628621f407SFrançois Tigeot 	intel_ring_emit(engine, flip_addr);
3638621f407SFrançois Tigeot 	intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
364f192107fSFrançois Tigeot 	/* turn overlay off */
3651487f786SFrançois Tigeot 	if (IS_I830(dev_priv)) {
36619df918dSFrançois Tigeot 		/* Workaround: Don't disable the overlay fully, since otherwise
36719df918dSFrançois Tigeot 		 * it dies on the next OVERLAY_ON cmd. */
3688621f407SFrançois Tigeot 		intel_ring_emit(engine, MI_NOOP);
3698621f407SFrançois Tigeot 		intel_ring_emit(engine, MI_NOOP);
3708621f407SFrançois Tigeot 		intel_ring_emit(engine, MI_NOOP);
37119df918dSFrançois Tigeot 	} else {
3728621f407SFrançois Tigeot 		intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
3738621f407SFrançois Tigeot 		intel_ring_emit(engine, flip_addr);
3748621f407SFrançois Tigeot 		intel_ring_emit(engine,
3758621f407SFrançois Tigeot 				MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
37619df918dSFrançois Tigeot 	}
3778621f407SFrançois Tigeot 	intel_ring_advance(engine);
378f192107fSFrançois Tigeot 
379a05eeebfSFrançois Tigeot 	return intel_overlay_do_wait_request(overlay, req, intel_overlay_off_tail);
380e3adcf8fSFrançois Tigeot }
381e3adcf8fSFrançois Tigeot 
382e3adcf8fSFrançois Tigeot /* recover from an interruption due to a signal
383e3adcf8fSFrançois Tigeot  * We have to be careful not to repeat work forever an make forward progess. */
384e3adcf8fSFrançois Tigeot static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
385e3adcf8fSFrançois Tigeot {
386e3adcf8fSFrançois Tigeot 	int ret;
387e3adcf8fSFrançois Tigeot 
3882c9916cdSFrançois Tigeot 	if (overlay->last_flip_req == NULL)
389e3adcf8fSFrançois Tigeot 		return 0;
390e3adcf8fSFrançois Tigeot 
3912c9916cdSFrançois Tigeot 	ret = i915_wait_request(overlay->last_flip_req);
392e3adcf8fSFrançois Tigeot 	if (ret)
393e3adcf8fSFrançois Tigeot 		return ret;
394e3adcf8fSFrançois Tigeot 
395e3adcf8fSFrançois Tigeot 	if (overlay->flip_tail)
396e3adcf8fSFrançois Tigeot 		overlay->flip_tail(overlay);
397e3adcf8fSFrançois Tigeot 
3982c9916cdSFrançois Tigeot 	i915_gem_request_assign(&overlay->last_flip_req, NULL);
399e3adcf8fSFrançois Tigeot 	return 0;
400e3adcf8fSFrançois Tigeot }
401e3adcf8fSFrançois Tigeot 
402e3adcf8fSFrançois Tigeot /* Wait for pending overlay flip and release old frame.
403e3adcf8fSFrançois Tigeot  * Needs to be called before the overlay register are changed
404e3adcf8fSFrançois Tigeot  * via intel_overlay_(un)map_regs
405e3adcf8fSFrançois Tigeot  */
406e3adcf8fSFrançois Tigeot static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
407e3adcf8fSFrançois Tigeot {
4081487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
4098621f407SFrançois Tigeot 	struct intel_engine_cs *engine = &dev_priv->engine[RCS];
410e3adcf8fSFrançois Tigeot 	int ret;
411e3adcf8fSFrançois Tigeot 
412303bf270SFrançois Tigeot 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
4132c9916cdSFrançois Tigeot 
414e3adcf8fSFrançois Tigeot 	/* Only wait if there is actually an old frame to release to
415e3adcf8fSFrançois Tigeot 	 * guarantee forward progress.
416e3adcf8fSFrançois Tigeot 	 */
417e3adcf8fSFrançois Tigeot 	if (!overlay->old_vid_bo)
418e3adcf8fSFrançois Tigeot 		return 0;
419e3adcf8fSFrançois Tigeot 
420e3adcf8fSFrançois Tigeot 	if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) {
421e3adcf8fSFrançois Tigeot 		/* synchronous slowpath */
422a05eeebfSFrançois Tigeot 		struct drm_i915_gem_request *req;
423a05eeebfSFrançois Tigeot 
4248621f407SFrançois Tigeot 		req = i915_gem_request_alloc(engine, NULL);
425c0e85e96SFrançois Tigeot 		if (IS_ERR(req))
426c0e85e96SFrançois Tigeot 			return PTR_ERR(req);
427e3adcf8fSFrançois Tigeot 
428a05eeebfSFrançois Tigeot 		ret = intel_ring_begin(req, 2);
429a05eeebfSFrançois Tigeot 		if (ret) {
4308621f407SFrançois Tigeot 			i915_add_request_no_flush(req);
431a05eeebfSFrançois Tigeot 			return ret;
432a05eeebfSFrançois Tigeot 		}
433a05eeebfSFrançois Tigeot 
4348621f407SFrançois Tigeot 		intel_ring_emit(engine,
4358621f407SFrançois Tigeot 				MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
4368621f407SFrançois Tigeot 		intel_ring_emit(engine, MI_NOOP);
4378621f407SFrançois Tigeot 		intel_ring_advance(engine);
438e3adcf8fSFrançois Tigeot 
439a05eeebfSFrançois Tigeot 		ret = intel_overlay_do_wait_request(overlay, req,
440e3adcf8fSFrançois Tigeot 						    intel_overlay_release_old_vid_tail);
441e3adcf8fSFrançois Tigeot 		if (ret)
442e3adcf8fSFrançois Tigeot 			return ret;
443e3adcf8fSFrançois Tigeot 	}
444e3adcf8fSFrançois Tigeot 
445e3adcf8fSFrançois Tigeot 	intel_overlay_release_old_vid_tail(overlay);
44624edb884SFrançois Tigeot 
44724edb884SFrançois Tigeot 
44824edb884SFrançois Tigeot 	i915_gem_track_fb(overlay->old_vid_bo, NULL,
44924edb884SFrançois Tigeot 			  INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
450e3adcf8fSFrançois Tigeot 	return 0;
451e3adcf8fSFrançois Tigeot }
452e3adcf8fSFrançois Tigeot 
4532c9916cdSFrançois Tigeot void intel_overlay_reset(struct drm_i915_private *dev_priv)
4542c9916cdSFrançois Tigeot {
4552c9916cdSFrançois Tigeot 	struct intel_overlay *overlay = dev_priv->overlay;
4562c9916cdSFrançois Tigeot 
4572c9916cdSFrançois Tigeot 	if (!overlay)
4582c9916cdSFrançois Tigeot 		return;
4592c9916cdSFrançois Tigeot 
4602c9916cdSFrançois Tigeot 	intel_overlay_release_old_vid(overlay);
4612c9916cdSFrançois Tigeot 
4622c9916cdSFrançois Tigeot 	overlay->last_flip_req = NULL;
4632c9916cdSFrançois Tigeot 	overlay->old_xscale = 0;
4642c9916cdSFrançois Tigeot 	overlay->old_yscale = 0;
4652c9916cdSFrançois Tigeot 	overlay->crtc = NULL;
4662c9916cdSFrançois Tigeot 	overlay->active = false;
4672c9916cdSFrançois Tigeot }
4682c9916cdSFrançois Tigeot 
469e3adcf8fSFrançois Tigeot struct put_image_params {
470e3adcf8fSFrançois Tigeot 	int format;
471e3adcf8fSFrançois Tigeot 	short dst_x;
472e3adcf8fSFrançois Tigeot 	short dst_y;
473e3adcf8fSFrançois Tigeot 	short dst_w;
474e3adcf8fSFrançois Tigeot 	short dst_h;
475e3adcf8fSFrançois Tigeot 	short src_w;
476e3adcf8fSFrançois Tigeot 	short src_scan_h;
477e3adcf8fSFrançois Tigeot 	short src_scan_w;
478e3adcf8fSFrançois Tigeot 	short src_h;
479e3adcf8fSFrançois Tigeot 	short stride_Y;
480e3adcf8fSFrançois Tigeot 	short stride_UV;
481e3adcf8fSFrançois Tigeot 	int offset_Y;
482e3adcf8fSFrançois Tigeot 	int offset_U;
483e3adcf8fSFrançois Tigeot 	int offset_V;
484e3adcf8fSFrançois Tigeot };
485e3adcf8fSFrançois Tigeot 
486e3adcf8fSFrançois Tigeot static int packed_depth_bytes(u32 format)
487e3adcf8fSFrançois Tigeot {
488e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
489e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
490e3adcf8fSFrançois Tigeot 		return 4;
491e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
492e3adcf8fSFrançois Tigeot 		/* return 6; not implemented */
493e3adcf8fSFrançois Tigeot 	default:
494e3adcf8fSFrançois Tigeot 		return -EINVAL;
495e3adcf8fSFrançois Tigeot 	}
496e3adcf8fSFrançois Tigeot }
497e3adcf8fSFrançois Tigeot 
498e3adcf8fSFrançois Tigeot static int packed_width_bytes(u32 format, short width)
499e3adcf8fSFrançois Tigeot {
500e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
501e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
502e3adcf8fSFrançois Tigeot 		return width << 1;
503e3adcf8fSFrançois Tigeot 	default:
504e3adcf8fSFrançois Tigeot 		return -EINVAL;
505e3adcf8fSFrançois Tigeot 	}
506e3adcf8fSFrançois Tigeot }
507e3adcf8fSFrançois Tigeot 
508e3adcf8fSFrançois Tigeot static int uv_hsubsampling(u32 format)
509e3adcf8fSFrançois Tigeot {
510e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
511e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
512e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV420:
513e3adcf8fSFrançois Tigeot 		return 2;
514e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
515e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV410:
516e3adcf8fSFrançois Tigeot 		return 4;
517e3adcf8fSFrançois Tigeot 	default:
518e3adcf8fSFrançois Tigeot 		return -EINVAL;
519e3adcf8fSFrançois Tigeot 	}
520e3adcf8fSFrançois Tigeot }
521e3adcf8fSFrançois Tigeot 
522e3adcf8fSFrançois Tigeot static int uv_vsubsampling(u32 format)
523e3adcf8fSFrançois Tigeot {
524e3adcf8fSFrançois Tigeot 	switch (format & I915_OVERLAY_DEPTH_MASK) {
525e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV420:
526e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV410:
527e3adcf8fSFrançois Tigeot 		return 2;
528e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV422:
529e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV411:
530e3adcf8fSFrançois Tigeot 		return 1;
531e3adcf8fSFrançois Tigeot 	default:
532e3adcf8fSFrançois Tigeot 		return -EINVAL;
533e3adcf8fSFrançois Tigeot 	}
534e3adcf8fSFrançois Tigeot }
535e3adcf8fSFrançois Tigeot 
5361487f786SFrançois Tigeot static u32 calc_swidthsw(struct drm_i915_private *dev_priv, u32 offset, u32 width)
537e3adcf8fSFrançois Tigeot {
538e3adcf8fSFrançois Tigeot 	u32 mask, shift, ret;
5391487f786SFrançois Tigeot 	if (IS_GEN2(dev_priv)) {
540e3adcf8fSFrançois Tigeot 		mask = 0x1f;
541e3adcf8fSFrançois Tigeot 		shift = 5;
542e3adcf8fSFrançois Tigeot 	} else {
543e3adcf8fSFrançois Tigeot 		mask = 0x3f;
544e3adcf8fSFrançois Tigeot 		shift = 6;
545e3adcf8fSFrançois Tigeot 	}
546e3adcf8fSFrançois Tigeot 	ret = ((offset + width + mask) >> shift) - (offset >> shift);
5471487f786SFrançois Tigeot 	if (!IS_GEN2(dev_priv))
548e3adcf8fSFrançois Tigeot 		ret <<= 1;
549e3adcf8fSFrançois Tigeot 	ret -= 1;
550e3adcf8fSFrançois Tigeot 	return ret << 2;
551e3adcf8fSFrançois Tigeot }
552e3adcf8fSFrançois Tigeot 
553e3adcf8fSFrançois Tigeot static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
554e3adcf8fSFrançois Tigeot 	0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
555e3adcf8fSFrançois Tigeot 	0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
556e3adcf8fSFrançois Tigeot 	0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
557e3adcf8fSFrançois Tigeot 	0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
558e3adcf8fSFrançois Tigeot 	0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
559e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
560e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
561e3adcf8fSFrançois Tigeot 	0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
562e3adcf8fSFrançois Tigeot 	0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
563e3adcf8fSFrançois Tigeot 	0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
564e3adcf8fSFrançois Tigeot 	0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
565e3adcf8fSFrançois Tigeot 	0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
566e3adcf8fSFrançois Tigeot 	0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
567e3adcf8fSFrançois Tigeot 	0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
568e3adcf8fSFrançois Tigeot 	0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
569e3adcf8fSFrançois Tigeot 	0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
570e3adcf8fSFrançois Tigeot 	0xb000, 0x3000, 0x0800, 0x3000, 0xb000
571e3adcf8fSFrançois Tigeot };
572e3adcf8fSFrançois Tigeot 
573e3adcf8fSFrançois Tigeot static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
574e3adcf8fSFrançois Tigeot 	0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
575e3adcf8fSFrançois Tigeot 	0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
576e3adcf8fSFrançois Tigeot 	0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
577e3adcf8fSFrançois Tigeot 	0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
578e3adcf8fSFrançois Tigeot 	0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
579e3adcf8fSFrançois Tigeot 	0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
580e3adcf8fSFrançois Tigeot 	0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
581e3adcf8fSFrançois Tigeot 	0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
582e3adcf8fSFrançois Tigeot 	0x3000, 0x0800, 0x3000
583e3adcf8fSFrançois Tigeot };
584e3adcf8fSFrançois Tigeot 
585e3440f96SFrançois Tigeot static void update_polyphase_filter(struct overlay_registers __iomem *regs)
586e3adcf8fSFrançois Tigeot {
587e3440f96SFrançois Tigeot 	memcpy_toio(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
588e3440f96SFrançois Tigeot 	memcpy_toio(regs->UV_HCOEFS, uv_static_hcoeffs,
589e3440f96SFrançois Tigeot 		    sizeof(uv_static_hcoeffs));
590e3adcf8fSFrançois Tigeot }
591e3adcf8fSFrançois Tigeot 
592e3adcf8fSFrançois Tigeot static bool update_scaling_factors(struct intel_overlay *overlay,
593e3440f96SFrançois Tigeot 				   struct overlay_registers __iomem *regs,
594e3adcf8fSFrançois Tigeot 				   struct put_image_params *params)
595e3adcf8fSFrançois Tigeot {
596e3adcf8fSFrançois Tigeot 	/* fixed point with a 12 bit shift */
597e3adcf8fSFrançois Tigeot 	u32 xscale, yscale, xscale_UV, yscale_UV;
598e3adcf8fSFrançois Tigeot #define FP_SHIFT 12
599e3adcf8fSFrançois Tigeot #define FRACT_MASK 0xfff
600e3adcf8fSFrançois Tigeot 	bool scale_changed = false;
601e3adcf8fSFrançois Tigeot 	int uv_hscale = uv_hsubsampling(params->format);
602e3adcf8fSFrançois Tigeot 	int uv_vscale = uv_vsubsampling(params->format);
603e3adcf8fSFrançois Tigeot 
604e3adcf8fSFrançois Tigeot 	if (params->dst_w > 1)
605e3adcf8fSFrançois Tigeot 		xscale = ((params->src_scan_w - 1) << FP_SHIFT)
606e3adcf8fSFrançois Tigeot 			/(params->dst_w);
607e3adcf8fSFrançois Tigeot 	else
608e3adcf8fSFrançois Tigeot 		xscale = 1 << FP_SHIFT;
609e3adcf8fSFrançois Tigeot 
610e3adcf8fSFrançois Tigeot 	if (params->dst_h > 1)
611e3adcf8fSFrançois Tigeot 		yscale = ((params->src_scan_h - 1) << FP_SHIFT)
612e3adcf8fSFrançois Tigeot 			/(params->dst_h);
613e3adcf8fSFrançois Tigeot 	else
614e3adcf8fSFrançois Tigeot 		yscale = 1 << FP_SHIFT;
615e3adcf8fSFrançois Tigeot 
616e3adcf8fSFrançois Tigeot 	/*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
617e3adcf8fSFrançois Tigeot 	xscale_UV = xscale/uv_hscale;
618e3adcf8fSFrançois Tigeot 	yscale_UV = yscale/uv_vscale;
619e3adcf8fSFrançois Tigeot 	/* make the Y scale to UV scale ratio an exact multiply */
620e3adcf8fSFrançois Tigeot 	xscale = xscale_UV * uv_hscale;
621e3adcf8fSFrançois Tigeot 	yscale = yscale_UV * uv_vscale;
622e3adcf8fSFrançois Tigeot 	/*} else {
623e3adcf8fSFrançois Tigeot 	  xscale_UV = 0;
624e3adcf8fSFrançois Tigeot 	  yscale_UV = 0;
625e3adcf8fSFrançois Tigeot 	  }*/
626e3adcf8fSFrançois Tigeot 
627e3adcf8fSFrançois Tigeot 	if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
628e3adcf8fSFrançois Tigeot 		scale_changed = true;
629e3adcf8fSFrançois Tigeot 	overlay->old_xscale = xscale;
630e3adcf8fSFrançois Tigeot 	overlay->old_yscale = yscale;
631e3adcf8fSFrançois Tigeot 
632e3440f96SFrançois Tigeot 	iowrite32(((yscale & FRACT_MASK) << 20) |
633e3adcf8fSFrançois Tigeot 		  ((xscale >> FP_SHIFT)  << 16) |
634e3440f96SFrançois Tigeot 		  ((xscale & FRACT_MASK) << 3),
635e3440f96SFrançois Tigeot 		 &regs->YRGBSCALE);
636e3adcf8fSFrançois Tigeot 
637e3440f96SFrançois Tigeot 	iowrite32(((yscale_UV & FRACT_MASK) << 20) |
638e3adcf8fSFrançois Tigeot 		  ((xscale_UV >> FP_SHIFT)  << 16) |
639e3440f96SFrançois Tigeot 		  ((xscale_UV & FRACT_MASK) << 3),
640e3440f96SFrançois Tigeot 		 &regs->UVSCALE);
641e3adcf8fSFrançois Tigeot 
642e3440f96SFrançois Tigeot 	iowrite32((((yscale    >> FP_SHIFT) << 16) |
643e3440f96SFrançois Tigeot 		   ((yscale_UV >> FP_SHIFT) << 0)),
644e3440f96SFrançois Tigeot 		 &regs->UVSCALEV);
645e3adcf8fSFrançois Tigeot 
646e3adcf8fSFrançois Tigeot 	if (scale_changed)
647e3adcf8fSFrançois Tigeot 		update_polyphase_filter(regs);
648e3adcf8fSFrançois Tigeot 
649e3adcf8fSFrançois Tigeot 	return scale_changed;
650e3adcf8fSFrançois Tigeot }
651e3adcf8fSFrançois Tigeot 
652e3adcf8fSFrançois Tigeot static void update_colorkey(struct intel_overlay *overlay,
653e3440f96SFrançois Tigeot 			    struct overlay_registers __iomem *regs)
654e3adcf8fSFrançois Tigeot {
655e3adcf8fSFrançois Tigeot 	u32 key = overlay->color_key;
65619c468b4SFrançois Tigeot 	u32 flags;
65719c468b4SFrançois Tigeot 
65819c468b4SFrançois Tigeot 	flags = 0;
65919c468b4SFrançois Tigeot 	if (overlay->color_key_enabled)
66019c468b4SFrançois Tigeot 		flags |= DST_KEY_ENABLE;
661e3adcf8fSFrançois Tigeot 
662ba55f2f5SFrançois Tigeot 	switch (overlay->crtc->base.primary->fb->bits_per_pixel) {
663e3adcf8fSFrançois Tigeot 	case 8:
66419c468b4SFrançois Tigeot 		key = 0;
66519c468b4SFrançois Tigeot 		flags |= CLK_RGB8I_MASK;
666e3adcf8fSFrançois Tigeot 		break;
667e3adcf8fSFrançois Tigeot 
668e3adcf8fSFrançois Tigeot 	case 16:
669ba55f2f5SFrançois Tigeot 		if (overlay->crtc->base.primary->fb->depth == 15) {
67019c468b4SFrançois Tigeot 			key = RGB15_TO_COLORKEY(key);
67119c468b4SFrançois Tigeot 			flags |= CLK_RGB15_MASK;
672e3adcf8fSFrançois Tigeot 		} else {
67319c468b4SFrançois Tigeot 			key = RGB16_TO_COLORKEY(key);
67419c468b4SFrançois Tigeot 			flags |= CLK_RGB16_MASK;
675e3adcf8fSFrançois Tigeot 		}
676e3adcf8fSFrançois Tigeot 		break;
677e3adcf8fSFrançois Tigeot 
678e3adcf8fSFrançois Tigeot 	case 24:
679e3adcf8fSFrançois Tigeot 	case 32:
68019c468b4SFrançois Tigeot 		flags |= CLK_RGB24_MASK;
681e3adcf8fSFrançois Tigeot 		break;
682e3adcf8fSFrançois Tigeot 	}
68319c468b4SFrançois Tigeot 
68419c468b4SFrançois Tigeot 	iowrite32(key, &regs->DCLRKV);
68519c468b4SFrançois Tigeot 	iowrite32(flags, &regs->DCLRKM);
686e3adcf8fSFrançois Tigeot }
687e3adcf8fSFrançois Tigeot 
688e3adcf8fSFrançois Tigeot static u32 overlay_cmd_reg(struct put_image_params *params)
689e3adcf8fSFrançois Tigeot {
690e3adcf8fSFrançois Tigeot 	u32 cmd = OCMD_ENABLE | OCMD_BUF_TYPE_FRAME | OCMD_BUFFER0;
691e3adcf8fSFrançois Tigeot 
692e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PLANAR) {
693e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_DEPTH_MASK) {
694e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV422:
695e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_422_PLANAR;
696e3adcf8fSFrançois Tigeot 			break;
697e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV420:
698e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_420_PLANAR;
699e3adcf8fSFrançois Tigeot 			break;
700e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV411:
701e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV410:
702e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_410_PLANAR;
703e3adcf8fSFrançois Tigeot 			break;
704e3adcf8fSFrançois Tigeot 		}
705e3adcf8fSFrançois Tigeot 	} else { /* YUV packed */
706e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_DEPTH_MASK) {
707e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV422:
708e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_422_PACKED;
709e3adcf8fSFrançois Tigeot 			break;
710e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_YUV411:
711e3adcf8fSFrançois Tigeot 			cmd |= OCMD_YUV_411_PACKED;
712e3adcf8fSFrançois Tigeot 			break;
713e3adcf8fSFrançois Tigeot 		}
714e3adcf8fSFrançois Tigeot 
715e3adcf8fSFrançois Tigeot 		switch (params->format & I915_OVERLAY_SWAP_MASK) {
716e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_NO_SWAP:
717e3adcf8fSFrançois Tigeot 			break;
718e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_UV_SWAP:
719e3adcf8fSFrançois Tigeot 			cmd |= OCMD_UV_SWAP;
720e3adcf8fSFrançois Tigeot 			break;
721e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_Y_SWAP:
722e3adcf8fSFrançois Tigeot 			cmd |= OCMD_Y_SWAP;
723e3adcf8fSFrançois Tigeot 			break;
724e3adcf8fSFrançois Tigeot 		case I915_OVERLAY_Y_AND_UV_SWAP:
725e3adcf8fSFrançois Tigeot 			cmd |= OCMD_Y_AND_UV_SWAP;
726e3adcf8fSFrançois Tigeot 			break;
727e3adcf8fSFrançois Tigeot 		}
728e3adcf8fSFrançois Tigeot 	}
729e3adcf8fSFrançois Tigeot 
730e3adcf8fSFrançois Tigeot 	return cmd;
731e3adcf8fSFrançois Tigeot }
732e3adcf8fSFrançois Tigeot 
733e3adcf8fSFrançois Tigeot static int intel_overlay_do_put_image(struct intel_overlay *overlay,
734e3adcf8fSFrançois Tigeot 				      struct drm_i915_gem_object *new_bo,
735e3adcf8fSFrançois Tigeot 				      struct put_image_params *params)
736e3adcf8fSFrançois Tigeot {
737e3adcf8fSFrançois Tigeot 	int ret, tmp_width;
738e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
739e3adcf8fSFrançois Tigeot 	bool scale_changed = false;
7401487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
741e3440f96SFrançois Tigeot 	u32 swidth, swidthsw, sheight, ostride;
74224edb884SFrançois Tigeot 	enum i915_pipe pipe = overlay->crtc->pipe;
743e3adcf8fSFrançois Tigeot 
744303bf270SFrançois Tigeot 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
745303bf270SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex));
746e3adcf8fSFrançois Tigeot 
747e3adcf8fSFrançois Tigeot 	ret = intel_overlay_release_old_vid(overlay);
748e3adcf8fSFrançois Tigeot 	if (ret != 0)
749e3adcf8fSFrançois Tigeot 		return ret;
750e3adcf8fSFrançois Tigeot 
751aee94f86SFrançois Tigeot 	ret = i915_gem_object_pin_to_display_plane(new_bo, 0,
752477eb7f9SFrançois Tigeot 						   &i915_ggtt_view_normal);
753e3adcf8fSFrançois Tigeot 	if (ret != 0)
754e3440f96SFrançois Tigeot 		return ret;
755e3adcf8fSFrançois Tigeot 
756e3adcf8fSFrançois Tigeot 	ret = i915_gem_object_put_fence(new_bo);
757e3adcf8fSFrançois Tigeot 	if (ret)
758e3adcf8fSFrançois Tigeot 		goto out_unpin;
759e3adcf8fSFrançois Tigeot 
760e3adcf8fSFrançois Tigeot 	if (!overlay->active) {
761e3440f96SFrançois Tigeot 		u32 oconfig;
762e3adcf8fSFrançois Tigeot 		regs = intel_overlay_map_regs(overlay);
763e3adcf8fSFrançois Tigeot 		if (!regs) {
764e3adcf8fSFrançois Tigeot 			ret = -ENOMEM;
765e3adcf8fSFrançois Tigeot 			goto out_unpin;
766e3adcf8fSFrançois Tigeot 		}
767e3440f96SFrançois Tigeot 		oconfig = OCONF_CC_OUT_8BIT;
7681487f786SFrançois Tigeot 		if (IS_GEN4(dev_priv))
769e3440f96SFrançois Tigeot 			oconfig |= OCONF_CSC_MODE_BT709;
77024edb884SFrançois Tigeot 		oconfig |= pipe == 0 ?
771e3adcf8fSFrançois Tigeot 			OCONF_PIPE_A : OCONF_PIPE_B;
772e3440f96SFrançois Tigeot 		iowrite32(oconfig, &regs->OCONFIG);
773e3adcf8fSFrançois Tigeot 		intel_overlay_unmap_regs(overlay, regs);
774e3adcf8fSFrançois Tigeot 
775e3adcf8fSFrançois Tigeot 		ret = intel_overlay_on(overlay);
776e3adcf8fSFrançois Tigeot 		if (ret != 0)
777e3adcf8fSFrançois Tigeot 			goto out_unpin;
778e3adcf8fSFrançois Tigeot 	}
779e3adcf8fSFrançois Tigeot 
780e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
781e3adcf8fSFrançois Tigeot 	if (!regs) {
782e3adcf8fSFrançois Tigeot 		ret = -ENOMEM;
783e3adcf8fSFrançois Tigeot 		goto out_unpin;
784e3adcf8fSFrançois Tigeot 	}
785e3adcf8fSFrançois Tigeot 
786e3440f96SFrançois Tigeot 	iowrite32((params->dst_y << 16) | params->dst_x, &regs->DWINPOS);
787e3440f96SFrançois Tigeot 	iowrite32((params->dst_h << 16) | params->dst_w, &regs->DWINSZ);
788e3adcf8fSFrançois Tigeot 
789e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PACKED)
790e3adcf8fSFrançois Tigeot 		tmp_width = packed_width_bytes(params->format, params->src_w);
791e3adcf8fSFrançois Tigeot 	else
792e3adcf8fSFrançois Tigeot 		tmp_width = params->src_w;
793e3adcf8fSFrançois Tigeot 
794e3440f96SFrançois Tigeot 	swidth = params->src_w;
7951487f786SFrançois Tigeot 	swidthsw = calc_swidthsw(dev_priv, params->offset_Y, tmp_width);
796e3440f96SFrançois Tigeot 	sheight = params->src_h;
7979edbd4a0SFrançois Tigeot 	iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_Y, &regs->OBUF_0Y);
798e3440f96SFrançois Tigeot 	ostride = params->stride_Y;
799e3adcf8fSFrançois Tigeot 
800e3adcf8fSFrançois Tigeot 	if (params->format & I915_OVERLAY_YUV_PLANAR) {
801e3adcf8fSFrançois Tigeot 		int uv_hscale = uv_hsubsampling(params->format);
802e3adcf8fSFrançois Tigeot 		int uv_vscale = uv_vsubsampling(params->format);
803e3adcf8fSFrançois Tigeot 		u32 tmp_U, tmp_V;
804e3440f96SFrançois Tigeot 		swidth |= (params->src_w/uv_hscale) << 16;
8051487f786SFrançois Tigeot 		tmp_U = calc_swidthsw(dev_priv, params->offset_U,
806e3adcf8fSFrançois Tigeot 				      params->src_w/uv_hscale);
8071487f786SFrançois Tigeot 		tmp_V = calc_swidthsw(dev_priv, params->offset_V,
808e3adcf8fSFrançois Tigeot 				      params->src_w/uv_hscale);
809e3440f96SFrançois Tigeot 		swidthsw |= max_t(u32, tmp_U, tmp_V) << 16;
810e3440f96SFrançois Tigeot 		sheight |= (params->src_h/uv_vscale) << 16;
8119edbd4a0SFrançois Tigeot 		iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_U, &regs->OBUF_0U);
8129edbd4a0SFrançois Tigeot 		iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_V, &regs->OBUF_0V);
813e3440f96SFrançois Tigeot 		ostride |= params->stride_UV << 16;
814e3adcf8fSFrançois Tigeot 	}
815e3adcf8fSFrançois Tigeot 
816e3440f96SFrançois Tigeot 	iowrite32(swidth, &regs->SWIDTH);
817e3440f96SFrançois Tigeot 	iowrite32(swidthsw, &regs->SWIDTHSW);
818e3440f96SFrançois Tigeot 	iowrite32(sheight, &regs->SHEIGHT);
819e3440f96SFrançois Tigeot 	iowrite32(ostride, &regs->OSTRIDE);
820e3440f96SFrançois Tigeot 
821e3adcf8fSFrançois Tigeot 	scale_changed = update_scaling_factors(overlay, regs, params);
822e3adcf8fSFrançois Tigeot 
823e3adcf8fSFrançois Tigeot 	update_colorkey(overlay, regs);
824e3adcf8fSFrançois Tigeot 
825e3440f96SFrançois Tigeot 	iowrite32(overlay_cmd_reg(params), &regs->OCMD);
826e3adcf8fSFrançois Tigeot 
827e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
828e3adcf8fSFrançois Tigeot 
829e3adcf8fSFrançois Tigeot 	ret = intel_overlay_continue(overlay, scale_changed);
830e3adcf8fSFrançois Tigeot 	if (ret)
831e3adcf8fSFrançois Tigeot 		goto out_unpin;
832e3adcf8fSFrançois Tigeot 
83324edb884SFrançois Tigeot 	i915_gem_track_fb(overlay->vid_bo, new_bo,
83424edb884SFrançois Tigeot 			  INTEL_FRONTBUFFER_OVERLAY(pipe));
83524edb884SFrançois Tigeot 
836e3adcf8fSFrançois Tigeot 	overlay->old_vid_bo = overlay->vid_bo;
837e3adcf8fSFrançois Tigeot 	overlay->vid_bo = new_bo;
838e3adcf8fSFrançois Tigeot 
839303bf270SFrançois Tigeot 	intel_frontbuffer_flip(&dev_priv->drm,
840303bf270SFrançois Tigeot 			       INTEL_FRONTBUFFER_OVERLAY(pipe));
84124edb884SFrançois Tigeot 
842e3adcf8fSFrançois Tigeot 	return 0;
843e3adcf8fSFrançois Tigeot 
844e3adcf8fSFrançois Tigeot out_unpin:
845ba55f2f5SFrançois Tigeot 	i915_gem_object_ggtt_unpin(new_bo);
846e3adcf8fSFrançois Tigeot 	return ret;
847e3adcf8fSFrançois Tigeot }
848e3adcf8fSFrançois Tigeot 
849e3adcf8fSFrançois Tigeot int intel_overlay_switch_off(struct intel_overlay *overlay)
850e3adcf8fSFrançois Tigeot {
8511487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
852e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
853e3adcf8fSFrançois Tigeot 	int ret;
854e3adcf8fSFrançois Tigeot 
855303bf270SFrançois Tigeot 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
856303bf270SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex));
857e3adcf8fSFrançois Tigeot 
858e3adcf8fSFrançois Tigeot 	ret = intel_overlay_recover_from_interrupt(overlay);
859e3adcf8fSFrançois Tigeot 	if (ret != 0)
860e3adcf8fSFrançois Tigeot 		return ret;
861e3adcf8fSFrançois Tigeot 
862e3adcf8fSFrançois Tigeot 	if (!overlay->active)
863e3adcf8fSFrançois Tigeot 		return 0;
864e3adcf8fSFrançois Tigeot 
865e3adcf8fSFrançois Tigeot 	ret = intel_overlay_release_old_vid(overlay);
866e3adcf8fSFrançois Tigeot 	if (ret != 0)
867e3adcf8fSFrançois Tigeot 		return ret;
868e3adcf8fSFrançois Tigeot 
869e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
870e3440f96SFrançois Tigeot 	iowrite32(0, &regs->OCMD);
871e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
872e3adcf8fSFrançois Tigeot 
873e3adcf8fSFrançois Tigeot 	ret = intel_overlay_off(overlay);
874e3adcf8fSFrançois Tigeot 	if (ret != 0)
875e3adcf8fSFrançois Tigeot 		return ret;
876e3adcf8fSFrançois Tigeot 
877e3adcf8fSFrançois Tigeot 	intel_overlay_off_tail(overlay);
878e3adcf8fSFrançois Tigeot 	return 0;
879e3adcf8fSFrançois Tigeot }
880e3adcf8fSFrançois Tigeot 
881e3adcf8fSFrançois Tigeot static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
882e3adcf8fSFrançois Tigeot 					  struct intel_crtc *crtc)
883e3adcf8fSFrançois Tigeot {
884e3adcf8fSFrançois Tigeot 	if (!crtc->active)
885e3adcf8fSFrançois Tigeot 		return -EINVAL;
886e3adcf8fSFrançois Tigeot 
887e3adcf8fSFrançois Tigeot 	/* can't use the overlay with double wide pipe */
8882c9916cdSFrançois Tigeot 	if (crtc->config->double_wide)
889e3adcf8fSFrançois Tigeot 		return -EINVAL;
890e3adcf8fSFrançois Tigeot 
891e3adcf8fSFrançois Tigeot 	return 0;
892e3adcf8fSFrançois Tigeot }
893e3adcf8fSFrançois Tigeot 
894e3adcf8fSFrançois Tigeot static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
895e3adcf8fSFrançois Tigeot {
8961487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
897e3adcf8fSFrançois Tigeot 	u32 pfit_control = I915_READ(PFIT_CONTROL);
898e3adcf8fSFrançois Tigeot 	u32 ratio;
899e3adcf8fSFrançois Tigeot 
900e3adcf8fSFrançois Tigeot 	/* XXX: This is not the same logic as in the xorg driver, but more in
901e3adcf8fSFrançois Tigeot 	 * line with the intel documentation for the i965
902e3adcf8fSFrançois Tigeot 	 */
9031487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 4) {
904e3adcf8fSFrançois Tigeot 		/* on i965 use the PGM reg to read out the autoscaler values */
905e3adcf8fSFrançois Tigeot 		ratio = I915_READ(PFIT_PGM_RATIOS) >> PFIT_VERT_SCALE_SHIFT_965;
906e3adcf8fSFrançois Tigeot 	} else {
907e3adcf8fSFrançois Tigeot 		if (pfit_control & VERT_AUTO_SCALE)
908e3adcf8fSFrançois Tigeot 			ratio = I915_READ(PFIT_AUTO_RATIOS);
909e3adcf8fSFrançois Tigeot 		else
910e3adcf8fSFrançois Tigeot 			ratio = I915_READ(PFIT_PGM_RATIOS);
911e3adcf8fSFrançois Tigeot 		ratio >>= PFIT_VERT_SCALE_SHIFT;
912e3adcf8fSFrançois Tigeot 	}
913e3adcf8fSFrançois Tigeot 
914e3adcf8fSFrançois Tigeot 	overlay->pfit_vscale_ratio = ratio;
915e3adcf8fSFrançois Tigeot }
916e3adcf8fSFrançois Tigeot 
917e3adcf8fSFrançois Tigeot static int check_overlay_dst(struct intel_overlay *overlay,
918e3adcf8fSFrançois Tigeot 			     struct drm_intel_overlay_put_image *rec)
919e3adcf8fSFrançois Tigeot {
920e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode = &overlay->crtc->base.mode;
921e3adcf8fSFrançois Tigeot 
922e3adcf8fSFrançois Tigeot 	if (rec->dst_x < mode->hdisplay &&
923e3adcf8fSFrançois Tigeot 	    rec->dst_x + rec->dst_width <= mode->hdisplay &&
924e3adcf8fSFrançois Tigeot 	    rec->dst_y < mode->vdisplay &&
925e3adcf8fSFrançois Tigeot 	    rec->dst_y + rec->dst_height <= mode->vdisplay)
926e3adcf8fSFrançois Tigeot 		return 0;
927e3adcf8fSFrançois Tigeot 	else
928e3adcf8fSFrançois Tigeot 		return -EINVAL;
929e3adcf8fSFrançois Tigeot }
930e3adcf8fSFrançois Tigeot 
931e3adcf8fSFrançois Tigeot static int check_overlay_scaling(struct put_image_params *rec)
932e3adcf8fSFrançois Tigeot {
933e3adcf8fSFrançois Tigeot 	u32 tmp;
934e3adcf8fSFrançois Tigeot 
935e3adcf8fSFrançois Tigeot 	/* downscaling limit is 8.0 */
936e3adcf8fSFrançois Tigeot 	tmp = ((rec->src_scan_h << 16) / rec->dst_h) >> 16;
937e3adcf8fSFrançois Tigeot 	if (tmp > 7)
938e3adcf8fSFrançois Tigeot 		return -EINVAL;
939e3adcf8fSFrançois Tigeot 	tmp = ((rec->src_scan_w << 16) / rec->dst_w) >> 16;
940e3adcf8fSFrançois Tigeot 	if (tmp > 7)
941e3adcf8fSFrançois Tigeot 		return -EINVAL;
942e3adcf8fSFrançois Tigeot 
943e3adcf8fSFrançois Tigeot 	return 0;
944e3adcf8fSFrançois Tigeot }
945e3adcf8fSFrançois Tigeot 
9461487f786SFrançois Tigeot static int check_overlay_src(struct drm_i915_private *dev_priv,
947e3adcf8fSFrançois Tigeot 			     struct drm_intel_overlay_put_image *rec,
948e3adcf8fSFrançois Tigeot 			     struct drm_i915_gem_object *new_bo)
949e3adcf8fSFrançois Tigeot {
950e3adcf8fSFrançois Tigeot 	int uv_hscale = uv_hsubsampling(rec->flags);
951e3adcf8fSFrançois Tigeot 	int uv_vscale = uv_vsubsampling(rec->flags);
952e3adcf8fSFrançois Tigeot 	u32 stride_mask;
953e3adcf8fSFrançois Tigeot 	int depth;
954e3adcf8fSFrançois Tigeot 	u32 tmp;
955e3adcf8fSFrançois Tigeot 
956e3adcf8fSFrançois Tigeot 	/* check src dimensions */
9571487f786SFrançois Tigeot 	if (IS_845G(dev_priv) || IS_I830(dev_priv)) {
958e3adcf8fSFrançois Tigeot 		if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY ||
959e3adcf8fSFrançois Tigeot 		    rec->src_width  > IMAGE_MAX_WIDTH_LEGACY)
960e3adcf8fSFrançois Tigeot 			return -EINVAL;
961e3adcf8fSFrançois Tigeot 	} else {
962e3adcf8fSFrançois Tigeot 		if (rec->src_height > IMAGE_MAX_HEIGHT ||
963e3adcf8fSFrançois Tigeot 		    rec->src_width  > IMAGE_MAX_WIDTH)
964e3adcf8fSFrançois Tigeot 			return -EINVAL;
965e3adcf8fSFrançois Tigeot 	}
966e3adcf8fSFrançois Tigeot 
967e3adcf8fSFrançois Tigeot 	/* better safe than sorry, use 4 as the maximal subsampling ratio */
968e3adcf8fSFrançois Tigeot 	if (rec->src_height < N_VERT_Y_TAPS*4 ||
969e3adcf8fSFrançois Tigeot 	    rec->src_width  < N_HORIZ_Y_TAPS*4)
970e3adcf8fSFrançois Tigeot 		return -EINVAL;
971e3adcf8fSFrançois Tigeot 
972e3adcf8fSFrançois Tigeot 	/* check alignment constraints */
973e3adcf8fSFrançois Tigeot 	switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
974e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_RGB:
975e3adcf8fSFrançois Tigeot 		/* not implemented */
976e3adcf8fSFrançois Tigeot 		return -EINVAL;
977e3adcf8fSFrançois Tigeot 
978e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PACKED:
979e3adcf8fSFrançois Tigeot 		if (uv_vscale != 1)
980e3adcf8fSFrançois Tigeot 			return -EINVAL;
981e3adcf8fSFrançois Tigeot 
982e3adcf8fSFrançois Tigeot 		depth = packed_depth_bytes(rec->flags);
983e3adcf8fSFrançois Tigeot 		if (depth < 0)
984e3adcf8fSFrançois Tigeot 			return depth;
985e3adcf8fSFrançois Tigeot 
986e3adcf8fSFrançois Tigeot 		/* ignore UV planes */
987e3adcf8fSFrançois Tigeot 		rec->stride_UV = 0;
988e3adcf8fSFrançois Tigeot 		rec->offset_U = 0;
989e3adcf8fSFrançois Tigeot 		rec->offset_V = 0;
990e3adcf8fSFrançois Tigeot 		/* check pixel alignment */
991e3adcf8fSFrançois Tigeot 		if (rec->offset_Y % depth)
992e3adcf8fSFrançois Tigeot 			return -EINVAL;
993e3adcf8fSFrançois Tigeot 		break;
994e3adcf8fSFrançois Tigeot 
995e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PLANAR:
996e3adcf8fSFrançois Tigeot 		if (uv_vscale < 0 || uv_hscale < 0)
997e3adcf8fSFrançois Tigeot 			return -EINVAL;
998e3adcf8fSFrançois Tigeot 		/* no offset restrictions for planar formats */
999e3adcf8fSFrançois Tigeot 		break;
1000e3adcf8fSFrançois Tigeot 
1001e3adcf8fSFrançois Tigeot 	default:
1002e3adcf8fSFrançois Tigeot 		return -EINVAL;
1003e3adcf8fSFrançois Tigeot 	}
1004e3adcf8fSFrançois Tigeot 
1005e3adcf8fSFrançois Tigeot 	if (rec->src_width % uv_hscale)
1006e3adcf8fSFrançois Tigeot 		return -EINVAL;
1007e3adcf8fSFrançois Tigeot 
1008e3adcf8fSFrançois Tigeot 	/* stride checking */
10091487f786SFrançois Tigeot 	if (IS_I830(dev_priv) || IS_845G(dev_priv))
1010e3adcf8fSFrançois Tigeot 		stride_mask = 255;
1011e3adcf8fSFrançois Tigeot 	else
1012e3adcf8fSFrançois Tigeot 		stride_mask = 63;
1013e3adcf8fSFrançois Tigeot 
1014e3adcf8fSFrançois Tigeot 	if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
1015e3adcf8fSFrançois Tigeot 		return -EINVAL;
10161487f786SFrançois Tigeot 	if (IS_GEN4(dev_priv) && rec->stride_Y < 512)
1017e3adcf8fSFrançois Tigeot 		return -EINVAL;
1018e3adcf8fSFrançois Tigeot 
1019e3adcf8fSFrançois Tigeot 	tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
1020e3adcf8fSFrançois Tigeot 		4096 : 8192;
1021e3adcf8fSFrançois Tigeot 	if (rec->stride_Y > tmp || rec->stride_UV > 2*1024)
1022e3adcf8fSFrançois Tigeot 		return -EINVAL;
1023e3adcf8fSFrançois Tigeot 
1024e3adcf8fSFrançois Tigeot 	/* check buffer dimensions */
1025e3adcf8fSFrançois Tigeot 	switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
1026e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_RGB:
1027e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PACKED:
1028e3adcf8fSFrançois Tigeot 		/* always 4 Y values per depth pixels */
1029e3adcf8fSFrançois Tigeot 		if (packed_width_bytes(rec->flags, rec->src_width) > rec->stride_Y)
1030e3adcf8fSFrançois Tigeot 			return -EINVAL;
1031e3adcf8fSFrançois Tigeot 
1032e3adcf8fSFrançois Tigeot 		tmp = rec->stride_Y*rec->src_height;
1033e3adcf8fSFrançois Tigeot 		if (rec->offset_Y + tmp > new_bo->base.size)
1034e3adcf8fSFrançois Tigeot 			return -EINVAL;
1035e3adcf8fSFrançois Tigeot 		break;
1036e3adcf8fSFrançois Tigeot 
1037e3adcf8fSFrançois Tigeot 	case I915_OVERLAY_YUV_PLANAR:
1038e3adcf8fSFrançois Tigeot 		if (rec->src_width > rec->stride_Y)
1039e3adcf8fSFrançois Tigeot 			return -EINVAL;
1040e3adcf8fSFrançois Tigeot 		if (rec->src_width/uv_hscale > rec->stride_UV)
1041e3adcf8fSFrançois Tigeot 			return -EINVAL;
1042e3adcf8fSFrançois Tigeot 
1043e3adcf8fSFrançois Tigeot 		tmp = rec->stride_Y * rec->src_height;
1044e3adcf8fSFrançois Tigeot 		if (rec->offset_Y + tmp > new_bo->base.size)
1045e3adcf8fSFrançois Tigeot 			return -EINVAL;
1046e3adcf8fSFrançois Tigeot 
1047e3adcf8fSFrançois Tigeot 		tmp = rec->stride_UV * (rec->src_height / uv_vscale);
1048e3adcf8fSFrançois Tigeot 		if (rec->offset_U + tmp > new_bo->base.size ||
1049e3adcf8fSFrançois Tigeot 		    rec->offset_V + tmp > new_bo->base.size)
1050e3adcf8fSFrançois Tigeot 			return -EINVAL;
1051e3adcf8fSFrançois Tigeot 		break;
1052e3adcf8fSFrançois Tigeot 	}
1053e3adcf8fSFrançois Tigeot 
1054e3adcf8fSFrançois Tigeot 	return 0;
1055e3adcf8fSFrançois Tigeot }
1056e3adcf8fSFrançois Tigeot 
1057e3adcf8fSFrançois Tigeot /**
1058e3adcf8fSFrançois Tigeot  * Return the pipe currently connected to the panel fitter,
1059e3adcf8fSFrançois Tigeot  * or -1 if the panel fitter is not present or not in use
1060e3adcf8fSFrançois Tigeot  */
10611487f786SFrançois Tigeot static int intel_panel_fitter_pipe(struct drm_i915_private *dev_priv)
1062e3adcf8fSFrançois Tigeot {
1063e3adcf8fSFrançois Tigeot 	u32  pfit_control;
1064e3adcf8fSFrançois Tigeot 
1065e3adcf8fSFrançois Tigeot 	/* i830 doesn't have a panel fitter */
10661487f786SFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 3 &&
10671487f786SFrançois Tigeot 	    (IS_I830(dev_priv) || !IS_MOBILE(dev_priv)))
1068e3adcf8fSFrançois Tigeot 		return -1;
1069e3adcf8fSFrançois Tigeot 
1070e3adcf8fSFrançois Tigeot 	pfit_control = I915_READ(PFIT_CONTROL);
1071e3adcf8fSFrançois Tigeot 
1072e3adcf8fSFrançois Tigeot 	/* See if the panel fitter is in use */
1073e3adcf8fSFrançois Tigeot 	if ((pfit_control & PFIT_ENABLE) == 0)
1074e3adcf8fSFrançois Tigeot 		return -1;
1075e3adcf8fSFrançois Tigeot 
1076e3adcf8fSFrançois Tigeot 	/* 965 can place panel fitter on either pipe */
10771487f786SFrançois Tigeot 	if (IS_GEN4(dev_priv))
1078e3adcf8fSFrançois Tigeot 		return (pfit_control >> 29) & 0x3;
1079e3adcf8fSFrançois Tigeot 
1080e3adcf8fSFrançois Tigeot 	/* older chips can only use pipe 1 */
1081e3adcf8fSFrançois Tigeot 	return 1;
1082e3adcf8fSFrançois Tigeot }
1083e3adcf8fSFrançois Tigeot 
10841487f786SFrançois Tigeot int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data,
1085e3adcf8fSFrançois Tigeot 				  struct drm_file *file_priv)
1086e3adcf8fSFrançois Tigeot {
1087e3adcf8fSFrançois Tigeot 	struct drm_intel_overlay_put_image *put_image_rec = data;
1088*bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
1089e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
109024edb884SFrançois Tigeot 	struct drm_crtc *drmmode_crtc;
1091e3adcf8fSFrançois Tigeot 	struct intel_crtc *crtc;
1092e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *new_bo;
1093e3adcf8fSFrançois Tigeot 	struct put_image_params *params;
1094e3adcf8fSFrançois Tigeot 	int ret;
1095e3adcf8fSFrançois Tigeot 
1096e3adcf8fSFrançois Tigeot 	overlay = dev_priv->overlay;
1097e3adcf8fSFrançois Tigeot 	if (!overlay) {
1098e3adcf8fSFrançois Tigeot 		DRM_DEBUG("userspace bug: no overlay\n");
1099e3adcf8fSFrançois Tigeot 		return -ENODEV;
1100e3adcf8fSFrançois Tigeot 	}
1101e3adcf8fSFrançois Tigeot 
1102e3adcf8fSFrançois Tigeot 	if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
1103a2fdbec6SFrançois Tigeot 		drm_modeset_lock_all(dev);
1104a2fdbec6SFrançois Tigeot 		mutex_lock(&dev->struct_mutex);
1105e3adcf8fSFrançois Tigeot 
1106e3adcf8fSFrançois Tigeot 		ret = intel_overlay_switch_off(overlay);
1107e3adcf8fSFrançois Tigeot 
1108a2fdbec6SFrançois Tigeot 		mutex_unlock(&dev->struct_mutex);
1109a2fdbec6SFrançois Tigeot 		drm_modeset_unlock_all(dev);
1110e3adcf8fSFrançois Tigeot 
1111e3adcf8fSFrançois Tigeot 		return ret;
1112e3adcf8fSFrançois Tigeot 	}
1113e3adcf8fSFrançois Tigeot 
1114*bf017597SFrançois Tigeot 	params = kmalloc(sizeof(*params), M_DRM, GFP_KERNEL);
1115e3440f96SFrançois Tigeot 	if (!params)
1116e3440f96SFrançois Tigeot 		return -ENOMEM;
1117e3adcf8fSFrançois Tigeot 
111824edb884SFrançois Tigeot 	drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id);
111924edb884SFrançois Tigeot 	if (!drmmode_crtc) {
1120e3adcf8fSFrançois Tigeot 		ret = -ENOENT;
1121e3adcf8fSFrançois Tigeot 		goto out_free;
1122e3adcf8fSFrançois Tigeot 	}
112324edb884SFrançois Tigeot 	crtc = to_intel_crtc(drmmode_crtc);
1124e3adcf8fSFrançois Tigeot 
11258621f407SFrançois Tigeot 	new_bo = to_intel_bo(drm_gem_object_lookup(file_priv,
1126e3adcf8fSFrançois Tigeot 						   put_image_rec->bo_handle));
1127e3adcf8fSFrançois Tigeot 	if (&new_bo->base == NULL) {
1128e3adcf8fSFrançois Tigeot 		ret = -ENOENT;
1129e3adcf8fSFrançois Tigeot 		goto out_free;
1130e3adcf8fSFrançois Tigeot 	}
1131e3adcf8fSFrançois Tigeot 
1132a2fdbec6SFrançois Tigeot 	drm_modeset_lock_all(dev);
1133a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
1134e3adcf8fSFrançois Tigeot 
1135e3adcf8fSFrançois Tigeot 	if (new_bo->tiling_mode) {
1136ba55f2f5SFrançois Tigeot 		DRM_DEBUG_KMS("buffer used for overlay image can not be tiled\n");
1137e3adcf8fSFrançois Tigeot 		ret = -EINVAL;
1138e3adcf8fSFrançois Tigeot 		goto out_unlock;
1139e3adcf8fSFrançois Tigeot 	}
1140e3adcf8fSFrançois Tigeot 
1141e3adcf8fSFrançois Tigeot 	ret = intel_overlay_recover_from_interrupt(overlay);
1142e3adcf8fSFrançois Tigeot 	if (ret != 0)
1143e3adcf8fSFrançois Tigeot 		goto out_unlock;
1144e3adcf8fSFrançois Tigeot 
1145e3adcf8fSFrançois Tigeot 	if (overlay->crtc != crtc) {
1146e3adcf8fSFrançois Tigeot 		struct drm_display_mode *mode = &crtc->base.mode;
1147e3adcf8fSFrançois Tigeot 		ret = intel_overlay_switch_off(overlay);
1148e3adcf8fSFrançois Tigeot 		if (ret != 0)
1149e3adcf8fSFrançois Tigeot 			goto out_unlock;
1150e3adcf8fSFrançois Tigeot 
1151e3adcf8fSFrançois Tigeot 		ret = check_overlay_possible_on_crtc(overlay, crtc);
1152e3adcf8fSFrançois Tigeot 		if (ret != 0)
1153e3adcf8fSFrançois Tigeot 			goto out_unlock;
1154e3adcf8fSFrançois Tigeot 
1155e3adcf8fSFrançois Tigeot 		overlay->crtc = crtc;
1156e3adcf8fSFrançois Tigeot 		crtc->overlay = overlay;
1157e3adcf8fSFrançois Tigeot 
1158e3adcf8fSFrançois Tigeot 		/* line too wide, i.e. one-line-mode */
1159e3adcf8fSFrançois Tigeot 		if (mode->hdisplay > 1024 &&
11601487f786SFrançois Tigeot 		    intel_panel_fitter_pipe(dev_priv) == crtc->pipe) {
116119c468b4SFrançois Tigeot 			overlay->pfit_active = true;
1162e3adcf8fSFrançois Tigeot 			update_pfit_vscale_ratio(overlay);
1163e3adcf8fSFrançois Tigeot 		} else
116419c468b4SFrançois Tigeot 			overlay->pfit_active = false;
1165e3adcf8fSFrançois Tigeot 	}
1166e3adcf8fSFrançois Tigeot 
1167e3adcf8fSFrançois Tigeot 	ret = check_overlay_dst(overlay, put_image_rec);
1168e3adcf8fSFrançois Tigeot 	if (ret != 0)
1169e3adcf8fSFrançois Tigeot 		goto out_unlock;
1170e3adcf8fSFrançois Tigeot 
1171e3adcf8fSFrançois Tigeot 	if (overlay->pfit_active) {
1172e3adcf8fSFrançois Tigeot 		params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
1173e3adcf8fSFrançois Tigeot 				 overlay->pfit_vscale_ratio);
1174e3adcf8fSFrançois Tigeot 		/* shifting right rounds downwards, so add 1 */
1175e3adcf8fSFrançois Tigeot 		params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
1176e3adcf8fSFrançois Tigeot 				 overlay->pfit_vscale_ratio) + 1;
1177e3adcf8fSFrançois Tigeot 	} else {
1178e3adcf8fSFrançois Tigeot 		params->dst_y = put_image_rec->dst_y;
1179e3adcf8fSFrançois Tigeot 		params->dst_h = put_image_rec->dst_height;
1180e3adcf8fSFrançois Tigeot 	}
1181e3adcf8fSFrançois Tigeot 	params->dst_x = put_image_rec->dst_x;
1182e3adcf8fSFrançois Tigeot 	params->dst_w = put_image_rec->dst_width;
1183e3adcf8fSFrançois Tigeot 
1184e3adcf8fSFrançois Tigeot 	params->src_w = put_image_rec->src_width;
1185e3adcf8fSFrançois Tigeot 	params->src_h = put_image_rec->src_height;
1186e3adcf8fSFrançois Tigeot 	params->src_scan_w = put_image_rec->src_scan_width;
1187e3adcf8fSFrançois Tigeot 	params->src_scan_h = put_image_rec->src_scan_height;
1188e3adcf8fSFrançois Tigeot 	if (params->src_scan_h > params->src_h ||
1189e3adcf8fSFrançois Tigeot 	    params->src_scan_w > params->src_w) {
1190e3adcf8fSFrançois Tigeot 		ret = -EINVAL;
1191e3adcf8fSFrançois Tigeot 		goto out_unlock;
1192e3adcf8fSFrançois Tigeot 	}
1193e3adcf8fSFrançois Tigeot 
11941487f786SFrançois Tigeot 	ret = check_overlay_src(dev_priv, put_image_rec, new_bo);
1195e3adcf8fSFrançois Tigeot 	if (ret != 0)
1196e3adcf8fSFrançois Tigeot 		goto out_unlock;
1197e3adcf8fSFrançois Tigeot 	params->format = put_image_rec->flags & ~I915_OVERLAY_FLAGS_MASK;
1198e3adcf8fSFrançois Tigeot 	params->stride_Y = put_image_rec->stride_Y;
1199e3adcf8fSFrançois Tigeot 	params->stride_UV = put_image_rec->stride_UV;
1200e3adcf8fSFrançois Tigeot 	params->offset_Y = put_image_rec->offset_Y;
1201e3adcf8fSFrançois Tigeot 	params->offset_U = put_image_rec->offset_U;
1202e3adcf8fSFrançois Tigeot 	params->offset_V = put_image_rec->offset_V;
1203e3adcf8fSFrançois Tigeot 
1204e3adcf8fSFrançois Tigeot 	/* Check scaling after src size to prevent a divide-by-zero. */
1205e3adcf8fSFrançois Tigeot 	ret = check_overlay_scaling(params);
1206e3adcf8fSFrançois Tigeot 	if (ret != 0)
1207e3adcf8fSFrançois Tigeot 		goto out_unlock;
1208e3adcf8fSFrançois Tigeot 
1209e3adcf8fSFrançois Tigeot 	ret = intel_overlay_do_put_image(overlay, new_bo, params);
1210e3adcf8fSFrançois Tigeot 	if (ret != 0)
1211e3adcf8fSFrançois Tigeot 		goto out_unlock;
1212e3adcf8fSFrançois Tigeot 
1213a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
1214a2fdbec6SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1215e3adcf8fSFrançois Tigeot 
1216158486a6SFrançois Tigeot 	kfree(params);
1217e3adcf8fSFrançois Tigeot 
1218e3adcf8fSFrançois Tigeot 	return 0;
1219e3adcf8fSFrançois Tigeot 
1220e3adcf8fSFrançois Tigeot out_unlock:
1221a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
1222a2fdbec6SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1223e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference_unlocked(&new_bo->base);
1224e3adcf8fSFrançois Tigeot out_free:
1225158486a6SFrançois Tigeot 	kfree(params);
1226e3adcf8fSFrançois Tigeot 
1227e3adcf8fSFrançois Tigeot 	return ret;
1228e3adcf8fSFrançois Tigeot }
1229e3adcf8fSFrançois Tigeot 
1230e3adcf8fSFrançois Tigeot static void update_reg_attrs(struct intel_overlay *overlay,
1231e3440f96SFrançois Tigeot 			     struct overlay_registers __iomem *regs)
1232e3adcf8fSFrançois Tigeot {
1233e3440f96SFrançois Tigeot 	iowrite32((overlay->contrast << 18) | (overlay->brightness & 0xff),
1234e3440f96SFrançois Tigeot 		  &regs->OCLRC0);
1235e3440f96SFrançois Tigeot 	iowrite32(overlay->saturation, &regs->OCLRC1);
1236e3adcf8fSFrançois Tigeot }
1237e3adcf8fSFrançois Tigeot 
1238e3adcf8fSFrançois Tigeot static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
1239e3adcf8fSFrançois Tigeot {
1240e3adcf8fSFrançois Tigeot 	int i;
1241e3adcf8fSFrançois Tigeot 
1242e3adcf8fSFrançois Tigeot 	if (gamma1 & 0xff000000 || gamma2 & 0xff000000)
1243e3adcf8fSFrançois Tigeot 		return false;
1244e3adcf8fSFrançois Tigeot 
1245e3adcf8fSFrançois Tigeot 	for (i = 0; i < 3; i++) {
1246e3adcf8fSFrançois Tigeot 		if (((gamma1 >> i*8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
1247e3adcf8fSFrançois Tigeot 			return false;
1248e3adcf8fSFrançois Tigeot 	}
1249e3adcf8fSFrançois Tigeot 
1250e3adcf8fSFrançois Tigeot 	return true;
1251e3adcf8fSFrançois Tigeot }
1252e3adcf8fSFrançois Tigeot 
1253e3adcf8fSFrançois Tigeot static bool check_gamma5_errata(u32 gamma5)
1254e3adcf8fSFrançois Tigeot {
1255e3adcf8fSFrançois Tigeot 	int i;
1256e3adcf8fSFrançois Tigeot 
1257e3adcf8fSFrançois Tigeot 	for (i = 0; i < 3; i++) {
1258e3adcf8fSFrançois Tigeot 		if (((gamma5 >> i*8) & 0xff) == 0x80)
1259e3adcf8fSFrançois Tigeot 			return false;
1260e3adcf8fSFrançois Tigeot 	}
1261e3adcf8fSFrançois Tigeot 
1262e3adcf8fSFrançois Tigeot 	return true;
1263e3adcf8fSFrançois Tigeot }
1264e3adcf8fSFrançois Tigeot 
1265e3adcf8fSFrançois Tigeot static int check_gamma(struct drm_intel_overlay_attrs *attrs)
1266e3adcf8fSFrançois Tigeot {
1267e3adcf8fSFrançois Tigeot 	if (!check_gamma_bounds(0, attrs->gamma0) ||
1268e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma0, attrs->gamma1) ||
1269e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma1, attrs->gamma2) ||
1270e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma2, attrs->gamma3) ||
1271e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma3, attrs->gamma4) ||
1272e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma4, attrs->gamma5) ||
1273e3adcf8fSFrançois Tigeot 	    !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
1274e3adcf8fSFrançois Tigeot 		return -EINVAL;
1275e3adcf8fSFrançois Tigeot 
1276e3adcf8fSFrançois Tigeot 	if (!check_gamma5_errata(attrs->gamma5))
1277e3adcf8fSFrançois Tigeot 		return -EINVAL;
1278e3adcf8fSFrançois Tigeot 
1279e3adcf8fSFrançois Tigeot 	return 0;
1280e3adcf8fSFrançois Tigeot }
1281e3adcf8fSFrançois Tigeot 
12821487f786SFrançois Tigeot int intel_overlay_attrs_ioctl(struct drm_device *dev, void *data,
1283e3adcf8fSFrançois Tigeot 			      struct drm_file *file_priv)
1284e3adcf8fSFrançois Tigeot {
1285e3adcf8fSFrançois Tigeot 	struct drm_intel_overlay_attrs *attrs = data;
1286*bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
1287e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
1288e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
1289e3adcf8fSFrançois Tigeot 	int ret;
1290e3adcf8fSFrançois Tigeot 
1291e3adcf8fSFrançois Tigeot 	overlay = dev_priv->overlay;
1292e3adcf8fSFrançois Tigeot 	if (!overlay) {
1293e3adcf8fSFrançois Tigeot 		DRM_DEBUG("userspace bug: no overlay\n");
1294e3adcf8fSFrançois Tigeot 		return -ENODEV;
1295e3adcf8fSFrançois Tigeot 	}
1296e3adcf8fSFrançois Tigeot 
1297a2fdbec6SFrançois Tigeot 	drm_modeset_lock_all(dev);
1298a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
1299e3adcf8fSFrançois Tigeot 
1300e3adcf8fSFrançois Tigeot 	ret = -EINVAL;
1301e3adcf8fSFrançois Tigeot 	if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
1302e3adcf8fSFrançois Tigeot 		attrs->color_key  = overlay->color_key;
1303e3adcf8fSFrançois Tigeot 		attrs->brightness = overlay->brightness;
1304e3adcf8fSFrançois Tigeot 		attrs->contrast   = overlay->contrast;
1305e3adcf8fSFrançois Tigeot 		attrs->saturation = overlay->saturation;
1306e3adcf8fSFrançois Tigeot 
13071487f786SFrançois Tigeot 		if (!IS_GEN2(dev_priv)) {
1308e3adcf8fSFrançois Tigeot 			attrs->gamma0 = I915_READ(OGAMC0);
1309e3adcf8fSFrançois Tigeot 			attrs->gamma1 = I915_READ(OGAMC1);
1310e3adcf8fSFrançois Tigeot 			attrs->gamma2 = I915_READ(OGAMC2);
1311e3adcf8fSFrançois Tigeot 			attrs->gamma3 = I915_READ(OGAMC3);
1312e3adcf8fSFrançois Tigeot 			attrs->gamma4 = I915_READ(OGAMC4);
1313e3adcf8fSFrançois Tigeot 			attrs->gamma5 = I915_READ(OGAMC5);
1314e3adcf8fSFrançois Tigeot 		}
1315e3adcf8fSFrançois Tigeot 	} else {
1316e3adcf8fSFrançois Tigeot 		if (attrs->brightness < -128 || attrs->brightness > 127)
1317e3adcf8fSFrançois Tigeot 			goto out_unlock;
1318e3adcf8fSFrançois Tigeot 		if (attrs->contrast > 255)
1319e3adcf8fSFrançois Tigeot 			goto out_unlock;
1320e3adcf8fSFrançois Tigeot 		if (attrs->saturation > 1023)
1321e3adcf8fSFrançois Tigeot 			goto out_unlock;
1322e3adcf8fSFrançois Tigeot 
1323e3adcf8fSFrançois Tigeot 		overlay->color_key  = attrs->color_key;
1324e3adcf8fSFrançois Tigeot 		overlay->brightness = attrs->brightness;
1325e3adcf8fSFrançois Tigeot 		overlay->contrast   = attrs->contrast;
1326e3adcf8fSFrançois Tigeot 		overlay->saturation = attrs->saturation;
1327e3adcf8fSFrançois Tigeot 
1328e3adcf8fSFrançois Tigeot 		regs = intel_overlay_map_regs(overlay);
1329e3adcf8fSFrançois Tigeot 		if (!regs) {
1330e3adcf8fSFrançois Tigeot 			ret = -ENOMEM;
1331e3adcf8fSFrançois Tigeot 			goto out_unlock;
1332e3adcf8fSFrançois Tigeot 		}
1333e3adcf8fSFrançois Tigeot 
1334e3adcf8fSFrançois Tigeot 		update_reg_attrs(overlay, regs);
1335e3adcf8fSFrançois Tigeot 
1336e3adcf8fSFrançois Tigeot 		intel_overlay_unmap_regs(overlay, regs);
1337e3adcf8fSFrançois Tigeot 
1338e3adcf8fSFrançois Tigeot 		if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
13391487f786SFrançois Tigeot 			if (IS_GEN2(dev_priv))
1340e3adcf8fSFrançois Tigeot 				goto out_unlock;
1341e3adcf8fSFrançois Tigeot 
1342e3adcf8fSFrançois Tigeot 			if (overlay->active) {
1343e3adcf8fSFrançois Tigeot 				ret = -EBUSY;
1344e3adcf8fSFrançois Tigeot 				goto out_unlock;
1345e3adcf8fSFrançois Tigeot 			}
1346e3adcf8fSFrançois Tigeot 
1347e3adcf8fSFrançois Tigeot 			ret = check_gamma(attrs);
1348e3adcf8fSFrançois Tigeot 			if (ret)
1349e3adcf8fSFrançois Tigeot 				goto out_unlock;
1350e3adcf8fSFrançois Tigeot 
1351e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC0, attrs->gamma0);
1352e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC1, attrs->gamma1);
1353e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC2, attrs->gamma2);
1354e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC3, attrs->gamma3);
1355e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC4, attrs->gamma4);
1356e3adcf8fSFrançois Tigeot 			I915_WRITE(OGAMC5, attrs->gamma5);
1357e3adcf8fSFrançois Tigeot 		}
1358e3adcf8fSFrançois Tigeot 	}
135919c468b4SFrançois Tigeot 	overlay->color_key_enabled = (attrs->flags & I915_OVERLAY_DISABLE_DEST_COLORKEY) == 0;
1360e3adcf8fSFrançois Tigeot 
1361e3adcf8fSFrançois Tigeot 	ret = 0;
1362e3adcf8fSFrançois Tigeot out_unlock:
1363a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
1364a2fdbec6SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1365e3adcf8fSFrançois Tigeot 
1366e3adcf8fSFrançois Tigeot 	return ret;
1367e3adcf8fSFrançois Tigeot }
1368e3adcf8fSFrançois Tigeot 
13691487f786SFrançois Tigeot void intel_setup_overlay(struct drm_i915_private *dev_priv)
1370e3adcf8fSFrançois Tigeot {
1371e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay;
1372e3adcf8fSFrançois Tigeot 	struct drm_i915_gem_object *reg_bo;
1373e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
1374e3adcf8fSFrançois Tigeot 	int ret;
1375e3adcf8fSFrançois Tigeot 
13761487f786SFrançois Tigeot 	if (!HAS_OVERLAY(dev_priv))
1377e3adcf8fSFrançois Tigeot 		return;
1378e3adcf8fSFrançois Tigeot 
13799edbd4a0SFrançois Tigeot 	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
1380e3440f96SFrançois Tigeot 	if (!overlay)
1381e3440f96SFrançois Tigeot 		return;
1382e3440f96SFrançois Tigeot 
1383303bf270SFrançois Tigeot 	mutex_lock(&dev_priv->drm.struct_mutex);
1384e3440f96SFrançois Tigeot 	if (WARN_ON(dev_priv->overlay))
1385e3adcf8fSFrançois Tigeot 		goto out_free;
1386e3440f96SFrançois Tigeot 
13871487f786SFrançois Tigeot 	overlay->i915 = dev_priv;
1388e3adcf8fSFrançois Tigeot 
13899edbd4a0SFrançois Tigeot 	reg_bo = NULL;
13901487f786SFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(dev_priv))
1391303bf270SFrançois Tigeot 		reg_bo = i915_gem_object_create_stolen(&dev_priv->drm,
1392303bf270SFrançois Tigeot 						       PAGE_SIZE);
13939edbd4a0SFrançois Tigeot 	if (reg_bo == NULL)
1394303bf270SFrançois Tigeot 		reg_bo = i915_gem_object_create(&dev_priv->drm, PAGE_SIZE);
13951487f786SFrançois Tigeot 	if (IS_ERR(reg_bo))
1396e3adcf8fSFrançois Tigeot 		goto out_free;
1397e3adcf8fSFrançois Tigeot 	overlay->reg_bo = reg_bo;
1398e3adcf8fSFrançois Tigeot 
13991487f786SFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(dev_priv)) {
1400ba55f2f5SFrançois Tigeot 		ret = i915_gem_object_attach_phys(reg_bo, PAGE_SIZE);
1401e3adcf8fSFrançois Tigeot 		if (ret) {
1402e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to attach phys overlay regs\n");
1403e3adcf8fSFrançois Tigeot 			goto out_free_bo;
1404e3adcf8fSFrançois Tigeot 		}
1405ba55f2f5SFrançois Tigeot 		overlay->flip_addr = reg_bo->phys_handle->busaddr;
1406e3adcf8fSFrançois Tigeot 	} else {
1407ba55f2f5SFrançois Tigeot 		ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, PIN_MAPPABLE);
1408e3adcf8fSFrançois Tigeot 		if (ret) {
1409e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to pin overlay register bo\n");
1410e3adcf8fSFrançois Tigeot 			goto out_free_bo;
1411e3adcf8fSFrançois Tigeot 		}
14129edbd4a0SFrançois Tigeot 		overlay->flip_addr = i915_gem_obj_ggtt_offset(reg_bo);
1413e3adcf8fSFrançois Tigeot 
1414e3adcf8fSFrançois Tigeot 		ret = i915_gem_object_set_to_gtt_domain(reg_bo, true);
1415e3adcf8fSFrançois Tigeot 		if (ret) {
1416e3adcf8fSFrançois Tigeot 			DRM_ERROR("failed to move overlay register bo into the GTT\n");
1417e3adcf8fSFrançois Tigeot 			goto out_unpin_bo;
1418e3adcf8fSFrançois Tigeot 		}
1419e3adcf8fSFrançois Tigeot 	}
1420e3adcf8fSFrançois Tigeot 
1421e3adcf8fSFrançois Tigeot 	/* init all values */
1422e3adcf8fSFrançois Tigeot 	overlay->color_key = 0x0101fe;
142319c468b4SFrançois Tigeot 	overlay->color_key_enabled = true;
1424e3adcf8fSFrançois Tigeot 	overlay->brightness = -19;
1425e3adcf8fSFrançois Tigeot 	overlay->contrast = 75;
1426e3adcf8fSFrançois Tigeot 	overlay->saturation = 146;
1427e3adcf8fSFrançois Tigeot 
1428e3adcf8fSFrançois Tigeot 	regs = intel_overlay_map_regs(overlay);
1429e3adcf8fSFrançois Tigeot 	if (!regs)
1430e3adcf8fSFrançois Tigeot 		goto out_unpin_bo;
1431e3adcf8fSFrançois Tigeot 
1432e3440f96SFrançois Tigeot 	memset_io(regs, 0, sizeof(struct overlay_registers));
1433e3adcf8fSFrançois Tigeot 	update_polyphase_filter(regs);
1434e3adcf8fSFrançois Tigeot 	update_reg_attrs(overlay, regs);
1435e3adcf8fSFrançois Tigeot 
1436e3adcf8fSFrançois Tigeot 	intel_overlay_unmap_regs(overlay, regs);
1437e3adcf8fSFrançois Tigeot 
1438e3adcf8fSFrançois Tigeot 	dev_priv->overlay = overlay;
1439303bf270SFrançois Tigeot 	mutex_unlock(&dev_priv->drm.struct_mutex);
1440e3440f96SFrançois Tigeot 	DRM_INFO("initialized overlay support\n");
1441e3adcf8fSFrançois Tigeot 	return;
1442e3adcf8fSFrançois Tigeot 
1443e3adcf8fSFrançois Tigeot out_unpin_bo:
14441487f786SFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(dev_priv))
1445ba55f2f5SFrançois Tigeot 		i915_gem_object_ggtt_unpin(reg_bo);
1446e3adcf8fSFrançois Tigeot out_free_bo:
1447e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference(&reg_bo->base);
1448e3adcf8fSFrançois Tigeot out_free:
1449303bf270SFrançois Tigeot 	mutex_unlock(&dev_priv->drm.struct_mutex);
1450158486a6SFrançois Tigeot 	kfree(overlay);
1451e3adcf8fSFrançois Tigeot 	return;
1452e3adcf8fSFrançois Tigeot }
1453e3adcf8fSFrançois Tigeot 
14541487f786SFrançois Tigeot void intel_cleanup_overlay(struct drm_i915_private *dev_priv)
1455e3adcf8fSFrançois Tigeot {
1456e3adcf8fSFrançois Tigeot 	if (!dev_priv->overlay)
1457e3adcf8fSFrançois Tigeot 		return;
1458e3adcf8fSFrançois Tigeot 
1459e3adcf8fSFrançois Tigeot 	/* The bo's should be free'd by the generic code already.
1460e3adcf8fSFrançois Tigeot 	 * Furthermore modesetting teardown happens beforehand so the
1461e3adcf8fSFrançois Tigeot 	 * hardware should be off already */
146219c468b4SFrançois Tigeot 	WARN_ON(dev_priv->overlay->active);
1463e3adcf8fSFrançois Tigeot 
1464e3adcf8fSFrançois Tigeot 	drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base);
1465158486a6SFrançois Tigeot 	kfree(dev_priv->overlay);
1466e3adcf8fSFrançois Tigeot }
1467e3adcf8fSFrançois Tigeot 
1468e3adcf8fSFrançois Tigeot struct intel_overlay_error_state {
1469e3adcf8fSFrançois Tigeot 	struct overlay_registers regs;
1470e3adcf8fSFrançois Tigeot 	unsigned long base;
1471e3adcf8fSFrançois Tigeot 	u32 dovsta;
1472e3adcf8fSFrançois Tigeot 	u32 isr;
1473e3adcf8fSFrançois Tigeot };
1474e3adcf8fSFrançois Tigeot 
1475e3440f96SFrançois Tigeot static struct overlay_registers __iomem *
1476e3440f96SFrançois Tigeot intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
1477e3440f96SFrançois Tigeot {
14781487f786SFrançois Tigeot 	struct drm_i915_private *dev_priv = overlay->i915;
1479e3440f96SFrançois Tigeot 	struct overlay_registers __iomem *regs;
1480e3440f96SFrançois Tigeot 
14811487f786SFrançois Tigeot 	if (OVERLAY_NEEDS_PHYSICAL(dev_priv))
1482e3440f96SFrançois Tigeot 		/* Cast to make sparse happy, but it's wc memory anyway, so
1483e3440f96SFrançois Tigeot 		 * equivalent to the wc io mapping on X86. */
1484e3440f96SFrançois Tigeot 		regs = (struct overlay_registers __iomem *)
14852c9916cdSFrançois Tigeot 			overlay->reg_bo->phys_handle->vaddr;
1486e3440f96SFrançois Tigeot 	else
14871487f786SFrançois Tigeot 		regs = io_mapping_map_atomic_wc(dev_priv->ggtt.mappable,
14881487f786SFrançois Tigeot 						overlay->flip_addr);
1489e3440f96SFrançois Tigeot 
1490e3440f96SFrançois Tigeot 	return regs;
1491e3440f96SFrançois Tigeot }
1492e3440f96SFrançois Tigeot 
1493e3440f96SFrançois Tigeot static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay,
1494e3440f96SFrançois Tigeot 					struct overlay_registers __iomem *regs)
1495e3440f96SFrançois Tigeot {
14961487f786SFrançois Tigeot 	if (!OVERLAY_NEEDS_PHYSICAL(overlay->i915))
1497e3440f96SFrançois Tigeot 		io_mapping_unmap_atomic(regs);
1498e3440f96SFrançois Tigeot }
1499e3440f96SFrançois Tigeot 
1500e3adcf8fSFrançois Tigeot struct intel_overlay_error_state *
15011487f786SFrançois Tigeot intel_overlay_capture_error_state(struct drm_i915_private *dev_priv)
1502e3adcf8fSFrançois Tigeot {
1503e3adcf8fSFrançois Tigeot 	struct intel_overlay *overlay = dev_priv->overlay;
1504e3adcf8fSFrançois Tigeot 	struct intel_overlay_error_state *error;
1505e3adcf8fSFrançois Tigeot 	struct overlay_registers __iomem *regs;
1506e3adcf8fSFrançois Tigeot 
1507e3adcf8fSFrançois Tigeot 	if (!overlay || !overlay->active)
1508e3adcf8fSFrançois Tigeot 		return NULL;
1509e3adcf8fSFrançois Tigeot 
1510*bf017597SFrançois Tigeot 	error = kmalloc(sizeof(*error), M_DRM, GFP_ATOMIC);
1511e3adcf8fSFrançois Tigeot 	if (error == NULL)
1512e3adcf8fSFrançois Tigeot 		return NULL;
1513e3adcf8fSFrançois Tigeot 
1514e3adcf8fSFrançois Tigeot 	error->dovsta = I915_READ(DOVSTA);
1515e3adcf8fSFrançois Tigeot 	error->isr = I915_READ(ISR);
15161487f786SFrançois Tigeot 	error->base = overlay->flip_addr;
1517e3adcf8fSFrançois Tigeot 
1518e3440f96SFrançois Tigeot 	regs = intel_overlay_map_regs_atomic(overlay);
1519e3adcf8fSFrançois Tigeot 	if (!regs)
1520e3adcf8fSFrançois Tigeot 		goto err;
1521e3adcf8fSFrançois Tigeot 
1522e3440f96SFrançois Tigeot 	memcpy_fromio(&error->regs, regs, sizeof(struct overlay_registers));
1523e3440f96SFrançois Tigeot 	intel_overlay_unmap_regs_atomic(overlay, regs);
1524e3adcf8fSFrançois Tigeot 
1525e3440f96SFrançois Tigeot 	return error;
1526e3adcf8fSFrançois Tigeot 
1527e3adcf8fSFrançois Tigeot err:
1528158486a6SFrançois Tigeot 	kfree(error);
1529e3440f96SFrançois Tigeot 	return NULL;
1530e3adcf8fSFrançois Tigeot }
1531e3adcf8fSFrançois Tigeot 
1532e3adcf8fSFrançois Tigeot void
15335d0b1887SFrançois Tigeot intel_overlay_print_error_state(struct drm_i915_error_state_buf *m,
15345d0b1887SFrançois Tigeot 				struct intel_overlay_error_state *error)
1535e3adcf8fSFrançois Tigeot {
15365d0b1887SFrançois Tigeot 	i915_error_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n",
1537e3adcf8fSFrançois Tigeot 			  error->dovsta, error->isr);
15385d0b1887SFrançois Tigeot 	i915_error_printf(m, "  Register file at 0x%08lx:\n",
1539e3adcf8fSFrançois Tigeot 			  error->base);
1540e3adcf8fSFrançois Tigeot 
15415d0b1887SFrançois Tigeot #define P(x) i915_error_printf(m, "    " #x ":	0x%08x\n", error->regs.x)
1542e3adcf8fSFrançois Tigeot 	P(OBUF_0Y);
1543e3adcf8fSFrançois Tigeot 	P(OBUF_1Y);
1544e3adcf8fSFrançois Tigeot 	P(OBUF_0U);
1545e3adcf8fSFrançois Tigeot 	P(OBUF_0V);
1546e3adcf8fSFrançois Tigeot 	P(OBUF_1U);
1547e3adcf8fSFrançois Tigeot 	P(OBUF_1V);
1548e3adcf8fSFrançois Tigeot 	P(OSTRIDE);
1549e3adcf8fSFrançois Tigeot 	P(YRGB_VPH);
1550e3adcf8fSFrançois Tigeot 	P(UV_VPH);
1551e3adcf8fSFrançois Tigeot 	P(HORZ_PH);
1552e3adcf8fSFrançois Tigeot 	P(INIT_PHS);
1553e3adcf8fSFrançois Tigeot 	P(DWINPOS);
1554e3adcf8fSFrançois Tigeot 	P(DWINSZ);
1555e3adcf8fSFrançois Tigeot 	P(SWIDTH);
1556e3adcf8fSFrançois Tigeot 	P(SWIDTHSW);
1557e3adcf8fSFrançois Tigeot 	P(SHEIGHT);
1558e3adcf8fSFrançois Tigeot 	P(YRGBSCALE);
1559e3adcf8fSFrançois Tigeot 	P(UVSCALE);
1560e3adcf8fSFrançois Tigeot 	P(OCLRC0);
1561e3adcf8fSFrançois Tigeot 	P(OCLRC1);
1562e3adcf8fSFrançois Tigeot 	P(DCLRKV);
1563e3adcf8fSFrançois Tigeot 	P(DCLRKM);
1564e3adcf8fSFrançois Tigeot 	P(SCLRKVH);
1565e3adcf8fSFrançois Tigeot 	P(SCLRKVL);
1566e3adcf8fSFrançois Tigeot 	P(SCLRKEN);
1567e3adcf8fSFrançois Tigeot 	P(OCONFIG);
1568e3adcf8fSFrançois Tigeot 	P(OCMD);
1569e3adcf8fSFrançois Tigeot 	P(OSTART_0Y);
1570e3adcf8fSFrançois Tigeot 	P(OSTART_1Y);
1571e3adcf8fSFrançois Tigeot 	P(OSTART_0U);
1572e3adcf8fSFrançois Tigeot 	P(OSTART_0V);
1573e3adcf8fSFrançois Tigeot 	P(OSTART_1U);
1574e3adcf8fSFrançois Tigeot 	P(OSTART_1V);
1575e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0Y);
1576e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1Y);
1577e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0U);
1578e3adcf8fSFrançois Tigeot 	P(OTILEOFF_0V);
1579e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1U);
1580e3adcf8fSFrançois Tigeot 	P(OTILEOFF_1V);
1581e3adcf8fSFrançois Tigeot 	P(FASTHSCALE);
1582e3adcf8fSFrançois Tigeot 	P(UVSCALEV);
1583e3adcf8fSFrançois Tigeot #undef P
1584e3adcf8fSFrançois Tigeot }
1585