xref: /linux/drivers/gpu/drm/exynos/exynos_mixer.c (revision af65c804877078a58cb02cc0f2ce4198401402fc)
1d8408326SSeung-Woo Kim /*
2d8408326SSeung-Woo Kim  * Copyright (C) 2011 Samsung Electronics Co.Ltd
3d8408326SSeung-Woo Kim  * Authors:
4d8408326SSeung-Woo Kim  * Seung-Woo Kim <sw0312.kim@samsung.com>
5d8408326SSeung-Woo Kim  *	Inki Dae <inki.dae@samsung.com>
6d8408326SSeung-Woo Kim  *	Joonyoung Shim <jy0922.shim@samsung.com>
7d8408326SSeung-Woo Kim  *
8d8408326SSeung-Woo Kim  * Based on drivers/media/video/s5p-tv/mixer_reg.c
9d8408326SSeung-Woo Kim  *
10d8408326SSeung-Woo Kim  * This program is free software; you can redistribute  it and/or modify it
11d8408326SSeung-Woo Kim  * under  the terms of  the GNU General  Public License as published by the
12d8408326SSeung-Woo Kim  * Free Software Foundation;  either version 2 of the  License, or (at your
13d8408326SSeung-Woo Kim  * option) any later version.
14d8408326SSeung-Woo Kim  *
15d8408326SSeung-Woo Kim  */
16d8408326SSeung-Woo Kim 
17760285e7SDavid Howells #include <drm/drmP.h>
18d8408326SSeung-Woo Kim 
19d8408326SSeung-Woo Kim #include "regs-mixer.h"
20d8408326SSeung-Woo Kim #include "regs-vp.h"
21d8408326SSeung-Woo Kim 
22d8408326SSeung-Woo Kim #include <linux/kernel.h>
23d8408326SSeung-Woo Kim #include <linux/spinlock.h>
24d8408326SSeung-Woo Kim #include <linux/wait.h>
25d8408326SSeung-Woo Kim #include <linux/i2c.h>
26d8408326SSeung-Woo Kim #include <linux/platform_device.h>
27d8408326SSeung-Woo Kim #include <linux/interrupt.h>
28d8408326SSeung-Woo Kim #include <linux/irq.h>
29d8408326SSeung-Woo Kim #include <linux/delay.h>
30d8408326SSeung-Woo Kim #include <linux/pm_runtime.h>
31d8408326SSeung-Woo Kim #include <linux/clk.h>
32d8408326SSeung-Woo Kim #include <linux/regulator/consumer.h>
333f1c781dSSachin Kamat #include <linux/of.h>
34d8408326SSeung-Woo Kim 
35d8408326SSeung-Woo Kim #include <drm/exynos_drm.h>
36d8408326SSeung-Woo Kim 
37d8408326SSeung-Woo Kim #include "exynos_drm_drv.h"
38663d8766SRahul Sharma #include "exynos_drm_crtc.h"
391055b39fSInki Dae #include "exynos_drm_iommu.h"
40f041b257SSean Paul #include "exynos_mixer.h"
4122b21ae6SJoonyoung Shim 
42f041b257SSean Paul #define get_mixer_manager(dev)	platform_get_drvdata(to_platform_device(dev))
43f041b257SSean Paul 
44f041b257SSean Paul #define MIXER_WIN_NR		3
45f041b257SSean Paul #define MIXER_DEFAULT_WIN	0
46d8408326SSeung-Woo Kim 
4722b21ae6SJoonyoung Shim struct hdmi_win_data {
4822b21ae6SJoonyoung Shim 	dma_addr_t		dma_addr;
4922b21ae6SJoonyoung Shim 	dma_addr_t		chroma_dma_addr;
5022b21ae6SJoonyoung Shim 	uint32_t		pixel_format;
5122b21ae6SJoonyoung Shim 	unsigned int		bpp;
5222b21ae6SJoonyoung Shim 	unsigned int		crtc_x;
5322b21ae6SJoonyoung Shim 	unsigned int		crtc_y;
5422b21ae6SJoonyoung Shim 	unsigned int		crtc_width;
5522b21ae6SJoonyoung Shim 	unsigned int		crtc_height;
5622b21ae6SJoonyoung Shim 	unsigned int		fb_x;
5722b21ae6SJoonyoung Shim 	unsigned int		fb_y;
5822b21ae6SJoonyoung Shim 	unsigned int		fb_width;
5922b21ae6SJoonyoung Shim 	unsigned int		fb_height;
608dcb96b6SSeung-Woo Kim 	unsigned int		src_width;
618dcb96b6SSeung-Woo Kim 	unsigned int		src_height;
6222b21ae6SJoonyoung Shim 	unsigned int		mode_width;
6322b21ae6SJoonyoung Shim 	unsigned int		mode_height;
6422b21ae6SJoonyoung Shim 	unsigned int		scan_flags;
65db43fd16SPrathyush K 	bool			enabled;
66db43fd16SPrathyush K 	bool			resume;
6722b21ae6SJoonyoung Shim };
6822b21ae6SJoonyoung Shim 
6922b21ae6SJoonyoung Shim struct mixer_resources {
7022b21ae6SJoonyoung Shim 	int			irq;
7122b21ae6SJoonyoung Shim 	void __iomem		*mixer_regs;
7222b21ae6SJoonyoung Shim 	void __iomem		*vp_regs;
7322b21ae6SJoonyoung Shim 	spinlock_t		reg_slock;
7422b21ae6SJoonyoung Shim 	struct clk		*mixer;
7522b21ae6SJoonyoung Shim 	struct clk		*vp;
7622b21ae6SJoonyoung Shim 	struct clk		*sclk_mixer;
7722b21ae6SJoonyoung Shim 	struct clk		*sclk_hdmi;
7822b21ae6SJoonyoung Shim 	struct clk		*sclk_dac;
7922b21ae6SJoonyoung Shim };
8022b21ae6SJoonyoung Shim 
811e123441SRahul Sharma enum mixer_version_id {
821e123441SRahul Sharma 	MXR_VER_0_0_0_16,
831e123441SRahul Sharma 	MXR_VER_16_0_33_0,
84def5e095SRahul Sharma 	MXR_VER_128_0_0_184,
851e123441SRahul Sharma };
861e123441SRahul Sharma 
8722b21ae6SJoonyoung Shim struct mixer_context {
884551789fSSean Paul 	struct platform_device *pdev;
89cf8fc4f1SJoonyoung Shim 	struct device		*dev;
901055b39fSInki Dae 	struct drm_device	*drm_dev;
9122b21ae6SJoonyoung Shim 	int			pipe;
9222b21ae6SJoonyoung Shim 	bool			interlace;
93cf8fc4f1SJoonyoung Shim 	bool			powered;
941b8e5747SRahul Sharma 	bool			vp_enabled;
95cf8fc4f1SJoonyoung Shim 	u32			int_en;
9622b21ae6SJoonyoung Shim 
97cf8fc4f1SJoonyoung Shim 	struct mutex		mixer_mutex;
9822b21ae6SJoonyoung Shim 	struct mixer_resources	mixer_res;
99a634dd54SJoonyoung Shim 	struct hdmi_win_data	win_data[MIXER_WIN_NR];
1001e123441SRahul Sharma 	enum mixer_version_id	mxr_ver;
1016e95d5e6SPrathyush K 	wait_queue_head_t	wait_vsync_queue;
1026e95d5e6SPrathyush K 	atomic_t		wait_vsync_event;
1031e123441SRahul Sharma };
1041e123441SRahul Sharma 
1051e123441SRahul Sharma struct mixer_drv_data {
1061e123441SRahul Sharma 	enum mixer_version_id	version;
1071b8e5747SRahul Sharma 	bool					is_vp_enabled;
10822b21ae6SJoonyoung Shim };
10922b21ae6SJoonyoung Shim 
110d8408326SSeung-Woo Kim static const u8 filter_y_horiz_tap8[] = {
111d8408326SSeung-Woo Kim 	0,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
112d8408326SSeung-Woo Kim 	-1,	-1,	-1,	-1,	-1,	0,	0,	0,
113d8408326SSeung-Woo Kim 	0,	2,	4,	5,	6,	6,	6,	6,
114d8408326SSeung-Woo Kim 	6,	5,	5,	4,	3,	2,	1,	1,
115d8408326SSeung-Woo Kim 	0,	-6,	-12,	-16,	-18,	-20,	-21,	-20,
116d8408326SSeung-Woo Kim 	-20,	-18,	-16,	-13,	-10,	-8,	-5,	-2,
117d8408326SSeung-Woo Kim 	127,	126,	125,	121,	114,	107,	99,	89,
118d8408326SSeung-Woo Kim 	79,	68,	57,	46,	35,	25,	16,	8,
119d8408326SSeung-Woo Kim };
120d8408326SSeung-Woo Kim 
121d8408326SSeung-Woo Kim static const u8 filter_y_vert_tap4[] = {
122d8408326SSeung-Woo Kim 	0,	-3,	-6,	-8,	-8,	-8,	-8,	-7,
123d8408326SSeung-Woo Kim 	-6,	-5,	-4,	-3,	-2,	-1,	-1,	0,
124d8408326SSeung-Woo Kim 	127,	126,	124,	118,	111,	102,	92,	81,
125d8408326SSeung-Woo Kim 	70,	59,	48,	37,	27,	19,	11,	5,
126d8408326SSeung-Woo Kim 	0,	5,	11,	19,	27,	37,	48,	59,
127d8408326SSeung-Woo Kim 	70,	81,	92,	102,	111,	118,	124,	126,
128d8408326SSeung-Woo Kim 	0,	0,	-1,	-1,	-2,	-3,	-4,	-5,
129d8408326SSeung-Woo Kim 	-6,	-7,	-8,	-8,	-8,	-8,	-6,	-3,
130d8408326SSeung-Woo Kim };
131d8408326SSeung-Woo Kim 
132d8408326SSeung-Woo Kim static const u8 filter_cr_horiz_tap4[] = {
133d8408326SSeung-Woo Kim 	0,	-3,	-6,	-8,	-8,	-8,	-8,	-7,
134d8408326SSeung-Woo Kim 	-6,	-5,	-4,	-3,	-2,	-1,	-1,	0,
135d8408326SSeung-Woo Kim 	127,	126,	124,	118,	111,	102,	92,	81,
136d8408326SSeung-Woo Kim 	70,	59,	48,	37,	27,	19,	11,	5,
137d8408326SSeung-Woo Kim };
138d8408326SSeung-Woo Kim 
139d8408326SSeung-Woo Kim static inline u32 vp_reg_read(struct mixer_resources *res, u32 reg_id)
140d8408326SSeung-Woo Kim {
141d8408326SSeung-Woo Kim 	return readl(res->vp_regs + reg_id);
142d8408326SSeung-Woo Kim }
143d8408326SSeung-Woo Kim 
144d8408326SSeung-Woo Kim static inline void vp_reg_write(struct mixer_resources *res, u32 reg_id,
145d8408326SSeung-Woo Kim 				 u32 val)
146d8408326SSeung-Woo Kim {
147d8408326SSeung-Woo Kim 	writel(val, res->vp_regs + reg_id);
148d8408326SSeung-Woo Kim }
149d8408326SSeung-Woo Kim 
150d8408326SSeung-Woo Kim static inline void vp_reg_writemask(struct mixer_resources *res, u32 reg_id,
151d8408326SSeung-Woo Kim 				 u32 val, u32 mask)
152d8408326SSeung-Woo Kim {
153d8408326SSeung-Woo Kim 	u32 old = vp_reg_read(res, reg_id);
154d8408326SSeung-Woo Kim 
155d8408326SSeung-Woo Kim 	val = (val & mask) | (old & ~mask);
156d8408326SSeung-Woo Kim 	writel(val, res->vp_regs + reg_id);
157d8408326SSeung-Woo Kim }
158d8408326SSeung-Woo Kim 
159d8408326SSeung-Woo Kim static inline u32 mixer_reg_read(struct mixer_resources *res, u32 reg_id)
160d8408326SSeung-Woo Kim {
161d8408326SSeung-Woo Kim 	return readl(res->mixer_regs + reg_id);
162d8408326SSeung-Woo Kim }
163d8408326SSeung-Woo Kim 
164d8408326SSeung-Woo Kim static inline void mixer_reg_write(struct mixer_resources *res, u32 reg_id,
165d8408326SSeung-Woo Kim 				 u32 val)
166d8408326SSeung-Woo Kim {
167d8408326SSeung-Woo Kim 	writel(val, res->mixer_regs + reg_id);
168d8408326SSeung-Woo Kim }
169d8408326SSeung-Woo Kim 
170d8408326SSeung-Woo Kim static inline void mixer_reg_writemask(struct mixer_resources *res,
171d8408326SSeung-Woo Kim 				 u32 reg_id, u32 val, u32 mask)
172d8408326SSeung-Woo Kim {
173d8408326SSeung-Woo Kim 	u32 old = mixer_reg_read(res, reg_id);
174d8408326SSeung-Woo Kim 
175d8408326SSeung-Woo Kim 	val = (val & mask) | (old & ~mask);
176d8408326SSeung-Woo Kim 	writel(val, res->mixer_regs + reg_id);
177d8408326SSeung-Woo Kim }
178d8408326SSeung-Woo Kim 
179d8408326SSeung-Woo Kim static void mixer_regs_dump(struct mixer_context *ctx)
180d8408326SSeung-Woo Kim {
181d8408326SSeung-Woo Kim #define DUMPREG(reg_id) \
182d8408326SSeung-Woo Kim do { \
183d8408326SSeung-Woo Kim 	DRM_DEBUG_KMS(#reg_id " = %08x\n", \
184d8408326SSeung-Woo Kim 		(u32)readl(ctx->mixer_res.mixer_regs + reg_id)); \
185d8408326SSeung-Woo Kim } while (0)
186d8408326SSeung-Woo Kim 
187d8408326SSeung-Woo Kim 	DUMPREG(MXR_STATUS);
188d8408326SSeung-Woo Kim 	DUMPREG(MXR_CFG);
189d8408326SSeung-Woo Kim 	DUMPREG(MXR_INT_EN);
190d8408326SSeung-Woo Kim 	DUMPREG(MXR_INT_STATUS);
191d8408326SSeung-Woo Kim 
192d8408326SSeung-Woo Kim 	DUMPREG(MXR_LAYER_CFG);
193d8408326SSeung-Woo Kim 	DUMPREG(MXR_VIDEO_CFG);
194d8408326SSeung-Woo Kim 
195d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_CFG);
196d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_BASE);
197d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_SPAN);
198d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_WH);
199d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_SXY);
200d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC0_DXY);
201d8408326SSeung-Woo Kim 
202d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_CFG);
203d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_BASE);
204d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_SPAN);
205d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_WH);
206d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_SXY);
207d8408326SSeung-Woo Kim 	DUMPREG(MXR_GRAPHIC1_DXY);
208d8408326SSeung-Woo Kim #undef DUMPREG
209d8408326SSeung-Woo Kim }
210d8408326SSeung-Woo Kim 
211d8408326SSeung-Woo Kim static void vp_regs_dump(struct mixer_context *ctx)
212d8408326SSeung-Woo Kim {
213d8408326SSeung-Woo Kim #define DUMPREG(reg_id) \
214d8408326SSeung-Woo Kim do { \
215d8408326SSeung-Woo Kim 	DRM_DEBUG_KMS(#reg_id " = %08x\n", \
216d8408326SSeung-Woo Kim 		(u32) readl(ctx->mixer_res.vp_regs + reg_id)); \
217d8408326SSeung-Woo Kim } while (0)
218d8408326SSeung-Woo Kim 
219d8408326SSeung-Woo Kim 	DUMPREG(VP_ENABLE);
220d8408326SSeung-Woo Kim 	DUMPREG(VP_SRESET);
221d8408326SSeung-Woo Kim 	DUMPREG(VP_SHADOW_UPDATE);
222d8408326SSeung-Woo Kim 	DUMPREG(VP_FIELD_ID);
223d8408326SSeung-Woo Kim 	DUMPREG(VP_MODE);
224d8408326SSeung-Woo Kim 	DUMPREG(VP_IMG_SIZE_Y);
225d8408326SSeung-Woo Kim 	DUMPREG(VP_IMG_SIZE_C);
226d8408326SSeung-Woo Kim 	DUMPREG(VP_PER_RATE_CTRL);
227d8408326SSeung-Woo Kim 	DUMPREG(VP_TOP_Y_PTR);
228d8408326SSeung-Woo Kim 	DUMPREG(VP_BOT_Y_PTR);
229d8408326SSeung-Woo Kim 	DUMPREG(VP_TOP_C_PTR);
230d8408326SSeung-Woo Kim 	DUMPREG(VP_BOT_C_PTR);
231d8408326SSeung-Woo Kim 	DUMPREG(VP_ENDIAN_MODE);
232d8408326SSeung-Woo Kim 	DUMPREG(VP_SRC_H_POSITION);
233d8408326SSeung-Woo Kim 	DUMPREG(VP_SRC_V_POSITION);
234d8408326SSeung-Woo Kim 	DUMPREG(VP_SRC_WIDTH);
235d8408326SSeung-Woo Kim 	DUMPREG(VP_SRC_HEIGHT);
236d8408326SSeung-Woo Kim 	DUMPREG(VP_DST_H_POSITION);
237d8408326SSeung-Woo Kim 	DUMPREG(VP_DST_V_POSITION);
238d8408326SSeung-Woo Kim 	DUMPREG(VP_DST_WIDTH);
239d8408326SSeung-Woo Kim 	DUMPREG(VP_DST_HEIGHT);
240d8408326SSeung-Woo Kim 	DUMPREG(VP_H_RATIO);
241d8408326SSeung-Woo Kim 	DUMPREG(VP_V_RATIO);
242d8408326SSeung-Woo Kim 
243d8408326SSeung-Woo Kim #undef DUMPREG
244d8408326SSeung-Woo Kim }
245d8408326SSeung-Woo Kim 
246d8408326SSeung-Woo Kim static inline void vp_filter_set(struct mixer_resources *res,
247d8408326SSeung-Woo Kim 		int reg_id, const u8 *data, unsigned int size)
248d8408326SSeung-Woo Kim {
249d8408326SSeung-Woo Kim 	/* assure 4-byte align */
250d8408326SSeung-Woo Kim 	BUG_ON(size & 3);
251d8408326SSeung-Woo Kim 	for (; size; size -= 4, reg_id += 4, data += 4) {
252d8408326SSeung-Woo Kim 		u32 val = (data[0] << 24) |  (data[1] << 16) |
253d8408326SSeung-Woo Kim 			(data[2] << 8) | data[3];
254d8408326SSeung-Woo Kim 		vp_reg_write(res, reg_id, val);
255d8408326SSeung-Woo Kim 	}
256d8408326SSeung-Woo Kim }
257d8408326SSeung-Woo Kim 
258d8408326SSeung-Woo Kim static void vp_default_filter(struct mixer_resources *res)
259d8408326SSeung-Woo Kim {
260d8408326SSeung-Woo Kim 	vp_filter_set(res, VP_POLY8_Y0_LL,
261e25e1b66SSachin Kamat 		filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
262d8408326SSeung-Woo Kim 	vp_filter_set(res, VP_POLY4_Y0_LL,
263e25e1b66SSachin Kamat 		filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
264d8408326SSeung-Woo Kim 	vp_filter_set(res, VP_POLY4_C0_LL,
265e25e1b66SSachin Kamat 		filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
266d8408326SSeung-Woo Kim }
267d8408326SSeung-Woo Kim 
268d8408326SSeung-Woo Kim static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
269d8408326SSeung-Woo Kim {
270d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
271d8408326SSeung-Woo Kim 
272d8408326SSeung-Woo Kim 	/* block update on vsync */
273d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_STATUS, enable ?
274d8408326SSeung-Woo Kim 			MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
275d8408326SSeung-Woo Kim 
2761b8e5747SRahul Sharma 	if (ctx->vp_enabled)
277d8408326SSeung-Woo Kim 		vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
278d8408326SSeung-Woo Kim 			VP_SHADOW_UPDATE_ENABLE : 0);
279d8408326SSeung-Woo Kim }
280d8408326SSeung-Woo Kim 
281d8408326SSeung-Woo Kim static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
282d8408326SSeung-Woo Kim {
283d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
284d8408326SSeung-Woo Kim 	u32 val;
285d8408326SSeung-Woo Kim 
286d8408326SSeung-Woo Kim 	/* choosing between interlace and progressive mode */
287d8408326SSeung-Woo Kim 	val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
288d8408326SSeung-Woo Kim 				MXR_CFG_SCAN_PROGRASSIVE);
289d8408326SSeung-Woo Kim 
290def5e095SRahul Sharma 	if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
291def5e095SRahul Sharma 		/* choosing between proper HD and SD mode */
29229630743SRahul Sharma 		if (height <= 480)
293d8408326SSeung-Woo Kim 			val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
29429630743SRahul Sharma 		else if (height <= 576)
295d8408326SSeung-Woo Kim 			val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
29629630743SRahul Sharma 		else if (height <= 720)
297d8408326SSeung-Woo Kim 			val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
29829630743SRahul Sharma 		else if (height <= 1080)
299d8408326SSeung-Woo Kim 			val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
300d8408326SSeung-Woo Kim 		else
301d8408326SSeung-Woo Kim 			val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
302def5e095SRahul Sharma 	}
303d8408326SSeung-Woo Kim 
304d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK);
305d8408326SSeung-Woo Kim }
306d8408326SSeung-Woo Kim 
307d8408326SSeung-Woo Kim static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
308d8408326SSeung-Woo Kim {
309d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
310d8408326SSeung-Woo Kim 	u32 val;
311d8408326SSeung-Woo Kim 
312d8408326SSeung-Woo Kim 	if (height == 480) {
313d8408326SSeung-Woo Kim 		val = MXR_CFG_RGB601_0_255;
314d8408326SSeung-Woo Kim 	} else if (height == 576) {
315d8408326SSeung-Woo Kim 		val = MXR_CFG_RGB601_0_255;
316d8408326SSeung-Woo Kim 	} else if (height == 720) {
317d8408326SSeung-Woo Kim 		val = MXR_CFG_RGB709_16_235;
318d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_Y,
319d8408326SSeung-Woo Kim 				(1 << 30) | (94 << 20) | (314 << 10) |
320d8408326SSeung-Woo Kim 				(32 << 0));
321d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CB,
322d8408326SSeung-Woo Kim 				(972 << 20) | (851 << 10) | (225 << 0));
323d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CR,
324d8408326SSeung-Woo Kim 				(225 << 20) | (820 << 10) | (1004 << 0));
325d8408326SSeung-Woo Kim 	} else if (height == 1080) {
326d8408326SSeung-Woo Kim 		val = MXR_CFG_RGB709_16_235;
327d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_Y,
328d8408326SSeung-Woo Kim 				(1 << 30) | (94 << 20) | (314 << 10) |
329d8408326SSeung-Woo Kim 				(32 << 0));
330d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CB,
331d8408326SSeung-Woo Kim 				(972 << 20) | (851 << 10) | (225 << 0));
332d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CR,
333d8408326SSeung-Woo Kim 				(225 << 20) | (820 << 10) | (1004 << 0));
334d8408326SSeung-Woo Kim 	} else {
335d8408326SSeung-Woo Kim 		val = MXR_CFG_RGB709_16_235;
336d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_Y,
337d8408326SSeung-Woo Kim 				(1 << 30) | (94 << 20) | (314 << 10) |
338d8408326SSeung-Woo Kim 				(32 << 0));
339d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CB,
340d8408326SSeung-Woo Kim 				(972 << 20) | (851 << 10) | (225 << 0));
341d8408326SSeung-Woo Kim 		mixer_reg_write(res, MXR_CM_COEFF_CR,
342d8408326SSeung-Woo Kim 				(225 << 20) | (820 << 10) | (1004 << 0));
343d8408326SSeung-Woo Kim 	}
344d8408326SSeung-Woo Kim 
345d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
346d8408326SSeung-Woo Kim }
347d8408326SSeung-Woo Kim 
348d8408326SSeung-Woo Kim static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
349d8408326SSeung-Woo Kim {
350d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
351d8408326SSeung-Woo Kim 	u32 val = enable ? ~0 : 0;
352d8408326SSeung-Woo Kim 
353d8408326SSeung-Woo Kim 	switch (win) {
354d8408326SSeung-Woo Kim 	case 0:
355d8408326SSeung-Woo Kim 		mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
356d8408326SSeung-Woo Kim 		break;
357d8408326SSeung-Woo Kim 	case 1:
358d8408326SSeung-Woo Kim 		mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
359d8408326SSeung-Woo Kim 		break;
360d8408326SSeung-Woo Kim 	case 2:
3611b8e5747SRahul Sharma 		if (ctx->vp_enabled) {
362d8408326SSeung-Woo Kim 			vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
3631b8e5747SRahul Sharma 			mixer_reg_writemask(res, MXR_CFG, val,
3641b8e5747SRahul Sharma 				MXR_CFG_VP_ENABLE);
3651b8e5747SRahul Sharma 		}
366d8408326SSeung-Woo Kim 		break;
367d8408326SSeung-Woo Kim 	}
368d8408326SSeung-Woo Kim }
369d8408326SSeung-Woo Kim 
370d8408326SSeung-Woo Kim static void mixer_run(struct mixer_context *ctx)
371d8408326SSeung-Woo Kim {
372d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
373d8408326SSeung-Woo Kim 
374d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
375d8408326SSeung-Woo Kim 
376d8408326SSeung-Woo Kim 	mixer_regs_dump(ctx);
377d8408326SSeung-Woo Kim }
378d8408326SSeung-Woo Kim 
379d8408326SSeung-Woo Kim static void vp_video_buffer(struct mixer_context *ctx, int win)
380d8408326SSeung-Woo Kim {
381d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
382d8408326SSeung-Woo Kim 	unsigned long flags;
383d8408326SSeung-Woo Kim 	struct hdmi_win_data *win_data;
384d8408326SSeung-Woo Kim 	unsigned int x_ratio, y_ratio;
385782953ecSYoungJun Cho 	unsigned int buf_num = 1;
386d8408326SSeung-Woo Kim 	dma_addr_t luma_addr[2], chroma_addr[2];
387d8408326SSeung-Woo Kim 	bool tiled_mode = false;
388d8408326SSeung-Woo Kim 	bool crcb_mode = false;
389d8408326SSeung-Woo Kim 	u32 val;
390d8408326SSeung-Woo Kim 
391d8408326SSeung-Woo Kim 	win_data = &ctx->win_data[win];
392d8408326SSeung-Woo Kim 
393d8408326SSeung-Woo Kim 	switch (win_data->pixel_format) {
394d8408326SSeung-Woo Kim 	case DRM_FORMAT_NV12MT:
395d8408326SSeung-Woo Kim 		tiled_mode = true;
396363b06aaSVille Syrjälä 	case DRM_FORMAT_NV12:
397d8408326SSeung-Woo Kim 		crcb_mode = false;
398d8408326SSeung-Woo Kim 		buf_num = 2;
399d8408326SSeung-Woo Kim 		break;
400d8408326SSeung-Woo Kim 	/* TODO: single buffer format NV12, NV21 */
401d8408326SSeung-Woo Kim 	default:
402d8408326SSeung-Woo Kim 		/* ignore pixel format at disable time */
403d8408326SSeung-Woo Kim 		if (!win_data->dma_addr)
404d8408326SSeung-Woo Kim 			break;
405d8408326SSeung-Woo Kim 
406d8408326SSeung-Woo Kim 		DRM_ERROR("pixel format for vp is wrong [%d].\n",
407d8408326SSeung-Woo Kim 				win_data->pixel_format);
408d8408326SSeung-Woo Kim 		return;
409d8408326SSeung-Woo Kim 	}
410d8408326SSeung-Woo Kim 
411d8408326SSeung-Woo Kim 	/* scaling feature: (src << 16) / dst */
4128dcb96b6SSeung-Woo Kim 	x_ratio = (win_data->src_width << 16) / win_data->crtc_width;
4138dcb96b6SSeung-Woo Kim 	y_ratio = (win_data->src_height << 16) / win_data->crtc_height;
414d8408326SSeung-Woo Kim 
415d8408326SSeung-Woo Kim 	if (buf_num == 2) {
416d8408326SSeung-Woo Kim 		luma_addr[0] = win_data->dma_addr;
417d8408326SSeung-Woo Kim 		chroma_addr[0] = win_data->chroma_dma_addr;
418d8408326SSeung-Woo Kim 	} else {
419d8408326SSeung-Woo Kim 		luma_addr[0] = win_data->dma_addr;
420d8408326SSeung-Woo Kim 		chroma_addr[0] = win_data->dma_addr
4218dcb96b6SSeung-Woo Kim 			+ (win_data->fb_width * win_data->fb_height);
422d8408326SSeung-Woo Kim 	}
423d8408326SSeung-Woo Kim 
424d8408326SSeung-Woo Kim 	if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
425d8408326SSeung-Woo Kim 		ctx->interlace = true;
426d8408326SSeung-Woo Kim 		if (tiled_mode) {
427d8408326SSeung-Woo Kim 			luma_addr[1] = luma_addr[0] + 0x40;
428d8408326SSeung-Woo Kim 			chroma_addr[1] = chroma_addr[0] + 0x40;
429d8408326SSeung-Woo Kim 		} else {
4308dcb96b6SSeung-Woo Kim 			luma_addr[1] = luma_addr[0] + win_data->fb_width;
4318dcb96b6SSeung-Woo Kim 			chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
432d8408326SSeung-Woo Kim 		}
433d8408326SSeung-Woo Kim 	} else {
434d8408326SSeung-Woo Kim 		ctx->interlace = false;
435d8408326SSeung-Woo Kim 		luma_addr[1] = 0;
436d8408326SSeung-Woo Kim 		chroma_addr[1] = 0;
437d8408326SSeung-Woo Kim 	}
438d8408326SSeung-Woo Kim 
439d8408326SSeung-Woo Kim 	spin_lock_irqsave(&res->reg_slock, flags);
440d8408326SSeung-Woo Kim 	mixer_vsync_set_update(ctx, false);
441d8408326SSeung-Woo Kim 
442d8408326SSeung-Woo Kim 	/* interlace or progressive scan mode */
443d8408326SSeung-Woo Kim 	val = (ctx->interlace ? ~0 : 0);
444d8408326SSeung-Woo Kim 	vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
445d8408326SSeung-Woo Kim 
446d8408326SSeung-Woo Kim 	/* setup format */
447d8408326SSeung-Woo Kim 	val = (crcb_mode ? VP_MODE_NV21 : VP_MODE_NV12);
448d8408326SSeung-Woo Kim 	val |= (tiled_mode ? VP_MODE_MEM_TILED : VP_MODE_MEM_LINEAR);
449d8408326SSeung-Woo Kim 	vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
450d8408326SSeung-Woo Kim 
451d8408326SSeung-Woo Kim 	/* setting size of input image */
4528dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
4538dcb96b6SSeung-Woo Kim 		VP_IMG_VSIZE(win_data->fb_height));
454d8408326SSeung-Woo Kim 	/* chroma height has to reduced by 2 to avoid chroma distorions */
4558dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
4568dcb96b6SSeung-Woo Kim 		VP_IMG_VSIZE(win_data->fb_height / 2));
457d8408326SSeung-Woo Kim 
4588dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
4598dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_SRC_HEIGHT, win_data->src_height);
460d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_SRC_H_POSITION,
4618dcb96b6SSeung-Woo Kim 			VP_SRC_H_POSITION_VAL(win_data->fb_x));
4628dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_SRC_V_POSITION, win_data->fb_y);
463d8408326SSeung-Woo Kim 
4648dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_DST_WIDTH, win_data->crtc_width);
4658dcb96b6SSeung-Woo Kim 	vp_reg_write(res, VP_DST_H_POSITION, win_data->crtc_x);
466d8408326SSeung-Woo Kim 	if (ctx->interlace) {
4678dcb96b6SSeung-Woo Kim 		vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height / 2);
4688dcb96b6SSeung-Woo Kim 		vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y / 2);
469d8408326SSeung-Woo Kim 	} else {
4708dcb96b6SSeung-Woo Kim 		vp_reg_write(res, VP_DST_HEIGHT, win_data->crtc_height);
4718dcb96b6SSeung-Woo Kim 		vp_reg_write(res, VP_DST_V_POSITION, win_data->crtc_y);
472d8408326SSeung-Woo Kim 	}
473d8408326SSeung-Woo Kim 
474d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_H_RATIO, x_ratio);
475d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_V_RATIO, y_ratio);
476d8408326SSeung-Woo Kim 
477d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
478d8408326SSeung-Woo Kim 
479d8408326SSeung-Woo Kim 	/* set buffer address to vp */
480d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_TOP_Y_PTR, luma_addr[0]);
481d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_BOT_Y_PTR, luma_addr[1]);
482d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_TOP_C_PTR, chroma_addr[0]);
483d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_BOT_C_PTR, chroma_addr[1]);
484d8408326SSeung-Woo Kim 
4858dcb96b6SSeung-Woo Kim 	mixer_cfg_scan(ctx, win_data->mode_height);
4868dcb96b6SSeung-Woo Kim 	mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
487d8408326SSeung-Woo Kim 	mixer_cfg_layer(ctx, win, true);
488d8408326SSeung-Woo Kim 	mixer_run(ctx);
489d8408326SSeung-Woo Kim 
490d8408326SSeung-Woo Kim 	mixer_vsync_set_update(ctx, true);
491d8408326SSeung-Woo Kim 	spin_unlock_irqrestore(&res->reg_slock, flags);
492d8408326SSeung-Woo Kim 
493d8408326SSeung-Woo Kim 	vp_regs_dump(ctx);
494d8408326SSeung-Woo Kim }
495d8408326SSeung-Woo Kim 
496aaf8b49eSRahul Sharma static void mixer_layer_update(struct mixer_context *ctx)
497aaf8b49eSRahul Sharma {
498aaf8b49eSRahul Sharma 	struct mixer_resources *res = &ctx->mixer_res;
499aaf8b49eSRahul Sharma 	u32 val;
500aaf8b49eSRahul Sharma 
501aaf8b49eSRahul Sharma 	val = mixer_reg_read(res, MXR_CFG);
502aaf8b49eSRahul Sharma 
503aaf8b49eSRahul Sharma 	/* allow one update per vsync only */
504aaf8b49eSRahul Sharma 	if (!(val & MXR_CFG_LAYER_UPDATE_COUNT_MASK))
505aaf8b49eSRahul Sharma 		mixer_reg_writemask(res, MXR_CFG, ~0, MXR_CFG_LAYER_UPDATE);
506aaf8b49eSRahul Sharma }
507aaf8b49eSRahul Sharma 
508d8408326SSeung-Woo Kim static void mixer_graph_buffer(struct mixer_context *ctx, int win)
509d8408326SSeung-Woo Kim {
510d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
511d8408326SSeung-Woo Kim 	unsigned long flags;
512d8408326SSeung-Woo Kim 	struct hdmi_win_data *win_data;
513d8408326SSeung-Woo Kim 	unsigned int x_ratio, y_ratio;
514d8408326SSeung-Woo Kim 	unsigned int src_x_offset, src_y_offset, dst_x_offset, dst_y_offset;
515d8408326SSeung-Woo Kim 	dma_addr_t dma_addr;
516d8408326SSeung-Woo Kim 	unsigned int fmt;
517d8408326SSeung-Woo Kim 	u32 val;
518d8408326SSeung-Woo Kim 
519d8408326SSeung-Woo Kim 	win_data = &ctx->win_data[win];
520d8408326SSeung-Woo Kim 
521d8408326SSeung-Woo Kim 	#define RGB565 4
522d8408326SSeung-Woo Kim 	#define ARGB1555 5
523d8408326SSeung-Woo Kim 	#define ARGB4444 6
524d8408326SSeung-Woo Kim 	#define ARGB8888 7
525d8408326SSeung-Woo Kim 
526d8408326SSeung-Woo Kim 	switch (win_data->bpp) {
527d8408326SSeung-Woo Kim 	case 16:
528d8408326SSeung-Woo Kim 		fmt = ARGB4444;
529d8408326SSeung-Woo Kim 		break;
530d8408326SSeung-Woo Kim 	case 32:
531d8408326SSeung-Woo Kim 		fmt = ARGB8888;
532d8408326SSeung-Woo Kim 		break;
533d8408326SSeung-Woo Kim 	default:
534d8408326SSeung-Woo Kim 		fmt = ARGB8888;
535d8408326SSeung-Woo Kim 	}
536d8408326SSeung-Woo Kim 
537d8408326SSeung-Woo Kim 	/* 2x scaling feature */
538d8408326SSeung-Woo Kim 	x_ratio = 0;
539d8408326SSeung-Woo Kim 	y_ratio = 0;
540d8408326SSeung-Woo Kim 
541d8408326SSeung-Woo Kim 	dst_x_offset = win_data->crtc_x;
542d8408326SSeung-Woo Kim 	dst_y_offset = win_data->crtc_y;
543d8408326SSeung-Woo Kim 
544d8408326SSeung-Woo Kim 	/* converting dma address base and source offset */
5458dcb96b6SSeung-Woo Kim 	dma_addr = win_data->dma_addr
5468dcb96b6SSeung-Woo Kim 		+ (win_data->fb_x * win_data->bpp >> 3)
5478dcb96b6SSeung-Woo Kim 		+ (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
548d8408326SSeung-Woo Kim 	src_x_offset = 0;
549d8408326SSeung-Woo Kim 	src_y_offset = 0;
550d8408326SSeung-Woo Kim 
551d8408326SSeung-Woo Kim 	if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE)
552d8408326SSeung-Woo Kim 		ctx->interlace = true;
553d8408326SSeung-Woo Kim 	else
554d8408326SSeung-Woo Kim 		ctx->interlace = false;
555d8408326SSeung-Woo Kim 
556d8408326SSeung-Woo Kim 	spin_lock_irqsave(&res->reg_slock, flags);
557d8408326SSeung-Woo Kim 	mixer_vsync_set_update(ctx, false);
558d8408326SSeung-Woo Kim 
559d8408326SSeung-Woo Kim 	/* setup format */
560d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_GRAPHIC_CFG(win),
561d8408326SSeung-Woo Kim 		MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
562d8408326SSeung-Woo Kim 
563d8408326SSeung-Woo Kim 	/* setup geometry */
5648dcb96b6SSeung-Woo Kim 	mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
565d8408326SSeung-Woo Kim 
566def5e095SRahul Sharma 	/* setup display size */
567def5e095SRahul Sharma 	if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
568def5e095SRahul Sharma 		win == MIXER_DEFAULT_WIN) {
569def5e095SRahul Sharma 		val  = MXR_MXR_RES_HEIGHT(win_data->fb_height);
570def5e095SRahul Sharma 		val |= MXR_MXR_RES_WIDTH(win_data->fb_width);
571def5e095SRahul Sharma 		mixer_reg_write(res, MXR_RESOLUTION, val);
572def5e095SRahul Sharma 	}
573def5e095SRahul Sharma 
5748dcb96b6SSeung-Woo Kim 	val  = MXR_GRP_WH_WIDTH(win_data->crtc_width);
5758dcb96b6SSeung-Woo Kim 	val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height);
576d8408326SSeung-Woo Kim 	val |= MXR_GRP_WH_H_SCALE(x_ratio);
577d8408326SSeung-Woo Kim 	val |= MXR_GRP_WH_V_SCALE(y_ratio);
578d8408326SSeung-Woo Kim 	mixer_reg_write(res, MXR_GRAPHIC_WH(win), val);
579d8408326SSeung-Woo Kim 
580d8408326SSeung-Woo Kim 	/* setup offsets in source image */
581d8408326SSeung-Woo Kim 	val  = MXR_GRP_SXY_SX(src_x_offset);
582d8408326SSeung-Woo Kim 	val |= MXR_GRP_SXY_SY(src_y_offset);
583d8408326SSeung-Woo Kim 	mixer_reg_write(res, MXR_GRAPHIC_SXY(win), val);
584d8408326SSeung-Woo Kim 
585d8408326SSeung-Woo Kim 	/* setup offsets in display image */
586d8408326SSeung-Woo Kim 	val  = MXR_GRP_DXY_DX(dst_x_offset);
587d8408326SSeung-Woo Kim 	val |= MXR_GRP_DXY_DY(dst_y_offset);
588d8408326SSeung-Woo Kim 	mixer_reg_write(res, MXR_GRAPHIC_DXY(win), val);
589d8408326SSeung-Woo Kim 
590d8408326SSeung-Woo Kim 	/* set buffer address to mixer */
591d8408326SSeung-Woo Kim 	mixer_reg_write(res, MXR_GRAPHIC_BASE(win), dma_addr);
592d8408326SSeung-Woo Kim 
5938dcb96b6SSeung-Woo Kim 	mixer_cfg_scan(ctx, win_data->mode_height);
5948dcb96b6SSeung-Woo Kim 	mixer_cfg_rgb_fmt(ctx, win_data->mode_height);
595d8408326SSeung-Woo Kim 	mixer_cfg_layer(ctx, win, true);
596aaf8b49eSRahul Sharma 
597aaf8b49eSRahul Sharma 	/* layer update mandatory for mixer 16.0.33.0 */
598def5e095SRahul Sharma 	if (ctx->mxr_ver == MXR_VER_16_0_33_0 ||
599def5e095SRahul Sharma 		ctx->mxr_ver == MXR_VER_128_0_0_184)
600aaf8b49eSRahul Sharma 		mixer_layer_update(ctx);
601aaf8b49eSRahul Sharma 
602d8408326SSeung-Woo Kim 	mixer_run(ctx);
603d8408326SSeung-Woo Kim 
604d8408326SSeung-Woo Kim 	mixer_vsync_set_update(ctx, true);
605d8408326SSeung-Woo Kim 	spin_unlock_irqrestore(&res->reg_slock, flags);
606d8408326SSeung-Woo Kim }
607d8408326SSeung-Woo Kim 
608d8408326SSeung-Woo Kim static void vp_win_reset(struct mixer_context *ctx)
609d8408326SSeung-Woo Kim {
610d8408326SSeung-Woo Kim 	struct mixer_resources *res = &ctx->mixer_res;
611d8408326SSeung-Woo Kim 	int tries = 100;
612d8408326SSeung-Woo Kim 
613d8408326SSeung-Woo Kim 	vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
614d8408326SSeung-Woo Kim 	for (tries = 100; tries; --tries) {
615d8408326SSeung-Woo Kim 		/* waiting until VP_SRESET_PROCESSING is 0 */
616d8408326SSeung-Woo Kim 		if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
617d8408326SSeung-Woo Kim 			break;
61809760ea3SSean Paul 		usleep_range(10000, 12000);
619d8408326SSeung-Woo Kim 	}
620d8408326SSeung-Woo Kim 	WARN(tries == 0, "failed to reset Video Processor\n");
621d8408326SSeung-Woo Kim }
622d8408326SSeung-Woo Kim 
623cf8fc4f1SJoonyoung Shim static void mixer_win_reset(struct mixer_context *ctx)
624cf8fc4f1SJoonyoung Shim {
625cf8fc4f1SJoonyoung Shim 	struct mixer_resources *res = &ctx->mixer_res;
626cf8fc4f1SJoonyoung Shim 	unsigned long flags;
627cf8fc4f1SJoonyoung Shim 	u32 val; /* value stored to register */
628cf8fc4f1SJoonyoung Shim 
629cf8fc4f1SJoonyoung Shim 	spin_lock_irqsave(&res->reg_slock, flags);
630cf8fc4f1SJoonyoung Shim 	mixer_vsync_set_update(ctx, false);
631cf8fc4f1SJoonyoung Shim 
632cf8fc4f1SJoonyoung Shim 	mixer_reg_writemask(res, MXR_CFG, MXR_CFG_DST_HDMI, MXR_CFG_DST_MASK);
633cf8fc4f1SJoonyoung Shim 
634cf8fc4f1SJoonyoung Shim 	/* set output in RGB888 mode */
635cf8fc4f1SJoonyoung Shim 	mixer_reg_writemask(res, MXR_CFG, MXR_CFG_OUT_RGB888, MXR_CFG_OUT_MASK);
636cf8fc4f1SJoonyoung Shim 
637cf8fc4f1SJoonyoung Shim 	/* 16 beat burst in DMA */
638cf8fc4f1SJoonyoung Shim 	mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
639cf8fc4f1SJoonyoung Shim 		MXR_STATUS_BURST_MASK);
640cf8fc4f1SJoonyoung Shim 
641cf8fc4f1SJoonyoung Shim 	/* setting default layer priority: layer1 > layer0 > video
642cf8fc4f1SJoonyoung Shim 	 * because typical usage scenario would be
643cf8fc4f1SJoonyoung Shim 	 * layer1 - OSD
644cf8fc4f1SJoonyoung Shim 	 * layer0 - framebuffer
645cf8fc4f1SJoonyoung Shim 	 * video - video overlay
646cf8fc4f1SJoonyoung Shim 	 */
647cf8fc4f1SJoonyoung Shim 	val = MXR_LAYER_CFG_GRP1_VAL(3);
648cf8fc4f1SJoonyoung Shim 	val |= MXR_LAYER_CFG_GRP0_VAL(2);
6491b8e5747SRahul Sharma 	if (ctx->vp_enabled)
650cf8fc4f1SJoonyoung Shim 		val |= MXR_LAYER_CFG_VP_VAL(1);
651cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_LAYER_CFG, val);
652cf8fc4f1SJoonyoung Shim 
653cf8fc4f1SJoonyoung Shim 	/* setting background color */
654cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_BG_COLOR0, 0x008080);
655cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
656cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
657cf8fc4f1SJoonyoung Shim 
658cf8fc4f1SJoonyoung Shim 	/* setting graphical layers */
659cf8fc4f1SJoonyoung Shim 	val  = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
660cf8fc4f1SJoonyoung Shim 	val |= MXR_GRP_CFG_WIN_BLEND_EN;
661cf8fc4f1SJoonyoung Shim 	val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
662cf8fc4f1SJoonyoung Shim 
6630377f4edSSean Paul 	/* Don't blend layer 0 onto the mixer background */
664cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_GRAPHIC_CFG(0), val);
6650377f4edSSean Paul 
6660377f4edSSean Paul 	/* Blend layer 1 into layer 0 */
6670377f4edSSean Paul 	val |= MXR_GRP_CFG_BLEND_PRE_MUL;
6680377f4edSSean Paul 	val |= MXR_GRP_CFG_PIXEL_BLEND_EN;
669cf8fc4f1SJoonyoung Shim 	mixer_reg_write(res, MXR_GRAPHIC_CFG(1), val);
670cf8fc4f1SJoonyoung Shim 
6715736603bSSeung-Woo Kim 	/* setting video layers */
6725736603bSSeung-Woo Kim 	val = MXR_GRP_CFG_ALPHA_VAL(0);
6735736603bSSeung-Woo Kim 	mixer_reg_write(res, MXR_VIDEO_CFG, val);
6745736603bSSeung-Woo Kim 
6751b8e5747SRahul Sharma 	if (ctx->vp_enabled) {
676cf8fc4f1SJoonyoung Shim 		/* configuration of Video Processor Registers */
677cf8fc4f1SJoonyoung Shim 		vp_win_reset(ctx);
678cf8fc4f1SJoonyoung Shim 		vp_default_filter(res);
6791b8e5747SRahul Sharma 	}
680cf8fc4f1SJoonyoung Shim 
681cf8fc4f1SJoonyoung Shim 	/* disable all layers */
682cf8fc4f1SJoonyoung Shim 	mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
683cf8fc4f1SJoonyoung Shim 	mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
6841b8e5747SRahul Sharma 	if (ctx->vp_enabled)
685cf8fc4f1SJoonyoung Shim 		mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
686cf8fc4f1SJoonyoung Shim 
687cf8fc4f1SJoonyoung Shim 	mixer_vsync_set_update(ctx, true);
688cf8fc4f1SJoonyoung Shim 	spin_unlock_irqrestore(&res->reg_slock, flags);
689cf8fc4f1SJoonyoung Shim }
690cf8fc4f1SJoonyoung Shim 
6914551789fSSean Paul static irqreturn_t mixer_irq_handler(int irq, void *arg)
6924551789fSSean Paul {
6934551789fSSean Paul 	struct mixer_context *ctx = arg;
6944551789fSSean Paul 	struct mixer_resources *res = &ctx->mixer_res;
6954551789fSSean Paul 	u32 val, base, shadow;
6964551789fSSean Paul 
6974551789fSSean Paul 	spin_lock(&res->reg_slock);
6984551789fSSean Paul 
6994551789fSSean Paul 	/* read interrupt status for handling and clearing flags for VSYNC */
7004551789fSSean Paul 	val = mixer_reg_read(res, MXR_INT_STATUS);
7014551789fSSean Paul 
7024551789fSSean Paul 	/* handling VSYNC */
7034551789fSSean Paul 	if (val & MXR_INT_STATUS_VSYNC) {
7044551789fSSean Paul 		/* interlace scan need to check shadow register */
7054551789fSSean Paul 		if (ctx->interlace) {
7064551789fSSean Paul 			base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
7074551789fSSean Paul 			shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
7084551789fSSean Paul 			if (base != shadow)
7094551789fSSean Paul 				goto out;
7104551789fSSean Paul 
7114551789fSSean Paul 			base = mixer_reg_read(res, MXR_GRAPHIC_BASE(1));
7124551789fSSean Paul 			shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(1));
7134551789fSSean Paul 			if (base != shadow)
7144551789fSSean Paul 				goto out;
7154551789fSSean Paul 		}
7164551789fSSean Paul 
7174551789fSSean Paul 		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
7184551789fSSean Paul 		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
7194551789fSSean Paul 
7204551789fSSean Paul 		/* set wait vsync event to zero and wake up queue. */
7214551789fSSean Paul 		if (atomic_read(&ctx->wait_vsync_event)) {
7224551789fSSean Paul 			atomic_set(&ctx->wait_vsync_event, 0);
7234551789fSSean Paul 			wake_up(&ctx->wait_vsync_queue);
7244551789fSSean Paul 		}
7254551789fSSean Paul 	}
7264551789fSSean Paul 
7274551789fSSean Paul out:
7284551789fSSean Paul 	/* clear interrupts */
7294551789fSSean Paul 	if (~val & MXR_INT_EN_VSYNC) {
7304551789fSSean Paul 		/* vsync interrupt use different bit for read and clear */
7314551789fSSean Paul 		val &= ~MXR_INT_EN_VSYNC;
7324551789fSSean Paul 		val |= MXR_INT_CLEAR_VSYNC;
7334551789fSSean Paul 	}
7344551789fSSean Paul 	mixer_reg_write(res, MXR_INT_STATUS, val);
7354551789fSSean Paul 
7364551789fSSean Paul 	spin_unlock(&res->reg_slock);
7374551789fSSean Paul 
7384551789fSSean Paul 	return IRQ_HANDLED;
7394551789fSSean Paul }
7404551789fSSean Paul 
7414551789fSSean Paul static int mixer_resources_init(struct mixer_context *mixer_ctx)
7424551789fSSean Paul {
7434551789fSSean Paul 	struct device *dev = &mixer_ctx->pdev->dev;
7444551789fSSean Paul 	struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
7454551789fSSean Paul 	struct resource *res;
7464551789fSSean Paul 	int ret;
7474551789fSSean Paul 
7484551789fSSean Paul 	spin_lock_init(&mixer_res->reg_slock);
7494551789fSSean Paul 
7504551789fSSean Paul 	mixer_res->mixer = devm_clk_get(dev, "mixer");
7514551789fSSean Paul 	if (IS_ERR(mixer_res->mixer)) {
7524551789fSSean Paul 		dev_err(dev, "failed to get clock 'mixer'\n");
7534551789fSSean Paul 		return -ENODEV;
7544551789fSSean Paul 	}
7554551789fSSean Paul 
7564551789fSSean Paul 	mixer_res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
7574551789fSSean Paul 	if (IS_ERR(mixer_res->sclk_hdmi)) {
7584551789fSSean Paul 		dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
7594551789fSSean Paul 		return -ENODEV;
7604551789fSSean Paul 	}
7614551789fSSean Paul 	res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 0);
7624551789fSSean Paul 	if (res == NULL) {
7634551789fSSean Paul 		dev_err(dev, "get memory resource failed.\n");
7644551789fSSean Paul 		return -ENXIO;
7654551789fSSean Paul 	}
7664551789fSSean Paul 
7674551789fSSean Paul 	mixer_res->mixer_regs = devm_ioremap(dev, res->start,
7684551789fSSean Paul 							resource_size(res));
7694551789fSSean Paul 	if (mixer_res->mixer_regs == NULL) {
7704551789fSSean Paul 		dev_err(dev, "register mapping failed.\n");
7714551789fSSean Paul 		return -ENXIO;
7724551789fSSean Paul 	}
7734551789fSSean Paul 
7744551789fSSean Paul 	res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_IRQ, 0);
7754551789fSSean Paul 	if (res == NULL) {
7764551789fSSean Paul 		dev_err(dev, "get interrupt resource failed.\n");
7774551789fSSean Paul 		return -ENXIO;
7784551789fSSean Paul 	}
7794551789fSSean Paul 
7804551789fSSean Paul 	ret = devm_request_irq(dev, res->start, mixer_irq_handler,
7814551789fSSean Paul 						0, "drm_mixer", mixer_ctx);
7824551789fSSean Paul 	if (ret) {
7834551789fSSean Paul 		dev_err(dev, "request interrupt failed.\n");
7844551789fSSean Paul 		return ret;
7854551789fSSean Paul 	}
7864551789fSSean Paul 	mixer_res->irq = res->start;
7874551789fSSean Paul 
7884551789fSSean Paul 	return 0;
7894551789fSSean Paul }
7904551789fSSean Paul 
7914551789fSSean Paul static int vp_resources_init(struct mixer_context *mixer_ctx)
7924551789fSSean Paul {
7934551789fSSean Paul 	struct device *dev = &mixer_ctx->pdev->dev;
7944551789fSSean Paul 	struct mixer_resources *mixer_res = &mixer_ctx->mixer_res;
7954551789fSSean Paul 	struct resource *res;
7964551789fSSean Paul 
7974551789fSSean Paul 	mixer_res->vp = devm_clk_get(dev, "vp");
7984551789fSSean Paul 	if (IS_ERR(mixer_res->vp)) {
7994551789fSSean Paul 		dev_err(dev, "failed to get clock 'vp'\n");
8004551789fSSean Paul 		return -ENODEV;
8014551789fSSean Paul 	}
8024551789fSSean Paul 	mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
8034551789fSSean Paul 	if (IS_ERR(mixer_res->sclk_mixer)) {
8044551789fSSean Paul 		dev_err(dev, "failed to get clock 'sclk_mixer'\n");
8054551789fSSean Paul 		return -ENODEV;
8064551789fSSean Paul 	}
8074551789fSSean Paul 	mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
8084551789fSSean Paul 	if (IS_ERR(mixer_res->sclk_dac)) {
8094551789fSSean Paul 		dev_err(dev, "failed to get clock 'sclk_dac'\n");
8104551789fSSean Paul 		return -ENODEV;
8114551789fSSean Paul 	}
8124551789fSSean Paul 
8134551789fSSean Paul 	if (mixer_res->sclk_hdmi)
8144551789fSSean Paul 		clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
8154551789fSSean Paul 
8164551789fSSean Paul 	res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
8174551789fSSean Paul 	if (res == NULL) {
8184551789fSSean Paul 		dev_err(dev, "get memory resource failed.\n");
8194551789fSSean Paul 		return -ENXIO;
8204551789fSSean Paul 	}
8214551789fSSean Paul 
8224551789fSSean Paul 	mixer_res->vp_regs = devm_ioremap(dev, res->start,
8234551789fSSean Paul 							resource_size(res));
8244551789fSSean Paul 	if (mixer_res->vp_regs == NULL) {
8254551789fSSean Paul 		dev_err(dev, "register mapping failed.\n");
8264551789fSSean Paul 		return -ENXIO;
8274551789fSSean Paul 	}
8284551789fSSean Paul 
8294551789fSSean Paul 	return 0;
8304551789fSSean Paul }
8314551789fSSean Paul 
832f041b257SSean Paul static int mixer_initialize(struct exynos_drm_manager *mgr,
833f041b257SSean Paul 			struct drm_device *drm_dev, int pipe)
8344551789fSSean Paul {
8354551789fSSean Paul 	int ret;
836f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
8374551789fSSean Paul 
8384551789fSSean Paul 	mixer_ctx->drm_dev = drm_dev;
839f041b257SSean Paul 	mixer_ctx->pipe = pipe;
8404551789fSSean Paul 
8414551789fSSean Paul 	/* acquire resources: regs, irqs, clocks */
8424551789fSSean Paul 	ret = mixer_resources_init(mixer_ctx);
8434551789fSSean Paul 	if (ret) {
8444551789fSSean Paul 		DRM_ERROR("mixer_resources_init failed ret=%d\n", ret);
8454551789fSSean Paul 		return ret;
8464551789fSSean Paul 	}
8474551789fSSean Paul 
8484551789fSSean Paul 	if (mixer_ctx->vp_enabled) {
8494551789fSSean Paul 		/* acquire vp resources: regs, irqs, clocks */
8504551789fSSean Paul 		ret = vp_resources_init(mixer_ctx);
8514551789fSSean Paul 		if (ret) {
8524551789fSSean Paul 			DRM_ERROR("vp_resources_init failed ret=%d\n", ret);
8534551789fSSean Paul 			return ret;
8544551789fSSean Paul 		}
8554551789fSSean Paul 	}
8564551789fSSean Paul 
857f041b257SSean Paul 	if (!is_drm_iommu_supported(mixer_ctx->drm_dev))
8581055b39fSInki Dae 		return 0;
859f041b257SSean Paul 
860f041b257SSean Paul 	return drm_iommu_attach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
8611055b39fSInki Dae }
8621055b39fSInki Dae 
863f041b257SSean Paul static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
864d8408326SSeung-Woo Kim {
865f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
866f041b257SSean Paul 
867f041b257SSean Paul 	if (is_drm_iommu_supported(mixer_ctx->drm_dev))
868f041b257SSean Paul 		drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
869f041b257SSean Paul }
870f041b257SSean Paul 
871f041b257SSean Paul static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
872f041b257SSean Paul {
873f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
874d8408326SSeung-Woo Kim 	struct mixer_resources *res = &mixer_ctx->mixer_res;
875d8408326SSeung-Woo Kim 
876f041b257SSean Paul 	if (!mixer_ctx->powered) {
877f041b257SSean Paul 		mixer_ctx->int_en |= MXR_INT_EN_VSYNC;
878f041b257SSean Paul 		return 0;
879f041b257SSean Paul 	}
880d8408326SSeung-Woo Kim 
881d8408326SSeung-Woo Kim 	/* enable vsync interrupt */
882d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC,
883d8408326SSeung-Woo Kim 			MXR_INT_EN_VSYNC);
884d8408326SSeung-Woo Kim 
885d8408326SSeung-Woo Kim 	return 0;
886d8408326SSeung-Woo Kim }
887d8408326SSeung-Woo Kim 
888f041b257SSean Paul static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
889d8408326SSeung-Woo Kim {
890f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
891d8408326SSeung-Woo Kim 	struct mixer_resources *res = &mixer_ctx->mixer_res;
892d8408326SSeung-Woo Kim 
893d8408326SSeung-Woo Kim 	/* disable vsync interrupt */
894d8408326SSeung-Woo Kim 	mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
895d8408326SSeung-Woo Kim }
896d8408326SSeung-Woo Kim 
897f041b257SSean Paul static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
898d8408326SSeung-Woo Kim 			struct exynos_drm_overlay *overlay)
899d8408326SSeung-Woo Kim {
900f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
901d8408326SSeung-Woo Kim 	struct hdmi_win_data *win_data;
902d8408326SSeung-Woo Kim 	int win;
903d8408326SSeung-Woo Kim 
904d8408326SSeung-Woo Kim 	if (!overlay) {
905d8408326SSeung-Woo Kim 		DRM_ERROR("overlay is NULL\n");
906d8408326SSeung-Woo Kim 		return;
907d8408326SSeung-Woo Kim 	}
908d8408326SSeung-Woo Kim 
909d8408326SSeung-Woo Kim 	DRM_DEBUG_KMS("set [%d]x[%d] at (%d,%d) to [%d]x[%d] at (%d,%d)\n",
910d8408326SSeung-Woo Kim 				 overlay->fb_width, overlay->fb_height,
911d8408326SSeung-Woo Kim 				 overlay->fb_x, overlay->fb_y,
912d8408326SSeung-Woo Kim 				 overlay->crtc_width, overlay->crtc_height,
913d8408326SSeung-Woo Kim 				 overlay->crtc_x, overlay->crtc_y);
914d8408326SSeung-Woo Kim 
915d8408326SSeung-Woo Kim 	win = overlay->zpos;
916d8408326SSeung-Woo Kim 	if (win == DEFAULT_ZPOS)
917a2ee151bSJoonyoung Shim 		win = MIXER_DEFAULT_WIN;
918d8408326SSeung-Woo Kim 
9191586d80cSKrzysztof Kozlowski 	if (win < 0 || win >= MIXER_WIN_NR) {
920cf8fc4f1SJoonyoung Shim 		DRM_ERROR("mixer window[%d] is wrong\n", win);
921d8408326SSeung-Woo Kim 		return;
922d8408326SSeung-Woo Kim 	}
923d8408326SSeung-Woo Kim 
924d8408326SSeung-Woo Kim 	win_data = &mixer_ctx->win_data[win];
925d8408326SSeung-Woo Kim 
926d8408326SSeung-Woo Kim 	win_data->dma_addr = overlay->dma_addr[0];
927d8408326SSeung-Woo Kim 	win_data->chroma_dma_addr = overlay->dma_addr[1];
928d8408326SSeung-Woo Kim 	win_data->pixel_format = overlay->pixel_format;
929d8408326SSeung-Woo Kim 	win_data->bpp = overlay->bpp;
930d8408326SSeung-Woo Kim 
931d8408326SSeung-Woo Kim 	win_data->crtc_x = overlay->crtc_x;
932d8408326SSeung-Woo Kim 	win_data->crtc_y = overlay->crtc_y;
933d8408326SSeung-Woo Kim 	win_data->crtc_width = overlay->crtc_width;
934d8408326SSeung-Woo Kim 	win_data->crtc_height = overlay->crtc_height;
935d8408326SSeung-Woo Kim 
936d8408326SSeung-Woo Kim 	win_data->fb_x = overlay->fb_x;
937d8408326SSeung-Woo Kim 	win_data->fb_y = overlay->fb_y;
938d8408326SSeung-Woo Kim 	win_data->fb_width = overlay->fb_width;
939d8408326SSeung-Woo Kim 	win_data->fb_height = overlay->fb_height;
9408dcb96b6SSeung-Woo Kim 	win_data->src_width = overlay->src_width;
9418dcb96b6SSeung-Woo Kim 	win_data->src_height = overlay->src_height;
942d8408326SSeung-Woo Kim 
943d8408326SSeung-Woo Kim 	win_data->mode_width = overlay->mode_width;
944d8408326SSeung-Woo Kim 	win_data->mode_height = overlay->mode_height;
945d8408326SSeung-Woo Kim 
946d8408326SSeung-Woo Kim 	win_data->scan_flags = overlay->scan_flag;
947d8408326SSeung-Woo Kim }
948d8408326SSeung-Woo Kim 
949f041b257SSean Paul static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
950d8408326SSeung-Woo Kim {
951f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
952f041b257SSean Paul 	int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
953d8408326SSeung-Woo Kim 
954cbc4c33dSYoungJun Cho 	DRM_DEBUG_KMS("win: %d\n", win);
955d8408326SSeung-Woo Kim 
956dda9012bSShirish S 	mutex_lock(&mixer_ctx->mixer_mutex);
957dda9012bSShirish S 	if (!mixer_ctx->powered) {
958dda9012bSShirish S 		mutex_unlock(&mixer_ctx->mixer_mutex);
959dda9012bSShirish S 		return;
960dda9012bSShirish S 	}
961dda9012bSShirish S 	mutex_unlock(&mixer_ctx->mixer_mutex);
962dda9012bSShirish S 
9631b8e5747SRahul Sharma 	if (win > 1 && mixer_ctx->vp_enabled)
964d8408326SSeung-Woo Kim 		vp_video_buffer(mixer_ctx, win);
965d8408326SSeung-Woo Kim 	else
966d8408326SSeung-Woo Kim 		mixer_graph_buffer(mixer_ctx, win);
967db43fd16SPrathyush K 
968db43fd16SPrathyush K 	mixer_ctx->win_data[win].enabled = true;
969d8408326SSeung-Woo Kim }
970d8408326SSeung-Woo Kim 
971f041b257SSean Paul static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
972d8408326SSeung-Woo Kim {
973f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
974d8408326SSeung-Woo Kim 	struct mixer_resources *res = &mixer_ctx->mixer_res;
975f041b257SSean Paul 	int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
976d8408326SSeung-Woo Kim 	unsigned long flags;
977d8408326SSeung-Woo Kim 
978cbc4c33dSYoungJun Cho 	DRM_DEBUG_KMS("win: %d\n", win);
979d8408326SSeung-Woo Kim 
980db43fd16SPrathyush K 	mutex_lock(&mixer_ctx->mixer_mutex);
981db43fd16SPrathyush K 	if (!mixer_ctx->powered) {
982db43fd16SPrathyush K 		mutex_unlock(&mixer_ctx->mixer_mutex);
983db43fd16SPrathyush K 		mixer_ctx->win_data[win].resume = false;
984db43fd16SPrathyush K 		return;
985db43fd16SPrathyush K 	}
986db43fd16SPrathyush K 	mutex_unlock(&mixer_ctx->mixer_mutex);
987db43fd16SPrathyush K 
988d8408326SSeung-Woo Kim 	spin_lock_irqsave(&res->reg_slock, flags);
989d8408326SSeung-Woo Kim 	mixer_vsync_set_update(mixer_ctx, false);
990d8408326SSeung-Woo Kim 
991d8408326SSeung-Woo Kim 	mixer_cfg_layer(mixer_ctx, win, false);
992d8408326SSeung-Woo Kim 
993d8408326SSeung-Woo Kim 	mixer_vsync_set_update(mixer_ctx, true);
994d8408326SSeung-Woo Kim 	spin_unlock_irqrestore(&res->reg_slock, flags);
995db43fd16SPrathyush K 
996db43fd16SPrathyush K 	mixer_ctx->win_data[win].enabled = false;
997d8408326SSeung-Woo Kim }
998d8408326SSeung-Woo Kim 
999f041b257SSean Paul static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
10000ea6822fSRahul Sharma {
1001f041b257SSean Paul 	struct mixer_context *mixer_ctx = mgr->ctx;
10028137a2e2SPrathyush K 
10036e95d5e6SPrathyush K 	mutex_lock(&mixer_ctx->mixer_mutex);
10046e95d5e6SPrathyush K 	if (!mixer_ctx->powered) {
10056e95d5e6SPrathyush K 		mutex_unlock(&mixer_ctx->mixer_mutex);
10066e95d5e6SPrathyush K 		return;
10076e95d5e6SPrathyush K 	}
10086e95d5e6SPrathyush K 	mutex_unlock(&mixer_ctx->mixer_mutex);
10096e95d5e6SPrathyush K 
10106e95d5e6SPrathyush K 	atomic_set(&mixer_ctx->wait_vsync_event, 1);
10116e95d5e6SPrathyush K 
10126e95d5e6SPrathyush K 	/*
10136e95d5e6SPrathyush K 	 * wait for MIXER to signal VSYNC interrupt or return after
10146e95d5e6SPrathyush K 	 * timeout which is set to 50ms (refresh rate of 20).
10156e95d5e6SPrathyush K 	 */
10166e95d5e6SPrathyush K 	if (!wait_event_timeout(mixer_ctx->wait_vsync_queue,
10176e95d5e6SPrathyush K 				!atomic_read(&mixer_ctx->wait_vsync_event),
1018bfd8303aSDaniel Vetter 				HZ/20))
10198137a2e2SPrathyush K 		DRM_DEBUG_KMS("vblank wait timed out.\n");
10208137a2e2SPrathyush K }
10218137a2e2SPrathyush K 
1022f041b257SSean Paul static void mixer_window_suspend(struct exynos_drm_manager *mgr)
1023db43fd16SPrathyush K {
1024f041b257SSean Paul 	struct mixer_context *ctx = mgr->ctx;
1025db43fd16SPrathyush K 	struct hdmi_win_data *win_data;
1026db43fd16SPrathyush K 	int i;
1027db43fd16SPrathyush K 
1028db43fd16SPrathyush K 	for (i = 0; i < MIXER_WIN_NR; i++) {
1029db43fd16SPrathyush K 		win_data = &ctx->win_data[i];
1030db43fd16SPrathyush K 		win_data->resume = win_data->enabled;
1031f041b257SSean Paul 		mixer_win_disable(mgr, i);
1032db43fd16SPrathyush K 	}
1033f041b257SSean Paul 	mixer_wait_for_vblank(mgr);
1034db43fd16SPrathyush K }
1035db43fd16SPrathyush K 
1036f041b257SSean Paul static void mixer_window_resume(struct exynos_drm_manager *mgr)
1037db43fd16SPrathyush K {
1038f041b257SSean Paul 	struct mixer_context *ctx = mgr->ctx;
1039db43fd16SPrathyush K 	struct hdmi_win_data *win_data;
1040db43fd16SPrathyush K 	int i;
1041db43fd16SPrathyush K 
1042db43fd16SPrathyush K 	for (i = 0; i < MIXER_WIN_NR; i++) {
1043db43fd16SPrathyush K 		win_data = &ctx->win_data[i];
1044db43fd16SPrathyush K 		win_data->enabled = win_data->resume;
1045db43fd16SPrathyush K 		win_data->resume = false;
104687244fa6SSean Paul 		if (win_data->enabled)
1047f041b257SSean Paul 			mixer_win_commit(mgr, i);
1048db43fd16SPrathyush K 	}
1049db43fd16SPrathyush K }
1050db43fd16SPrathyush K 
1051f041b257SSean Paul static void mixer_poweron(struct exynos_drm_manager *mgr)
1052db43fd16SPrathyush K {
1053f041b257SSean Paul 	struct mixer_context *ctx = mgr->ctx;
1054db43fd16SPrathyush K 	struct mixer_resources *res = &ctx->mixer_res;
1055db43fd16SPrathyush K 
1056db43fd16SPrathyush K 	mutex_lock(&ctx->mixer_mutex);
1057db43fd16SPrathyush K 	if (ctx->powered) {
1058db43fd16SPrathyush K 		mutex_unlock(&ctx->mixer_mutex);
1059db43fd16SPrathyush K 		return;
1060db43fd16SPrathyush K 	}
1061db43fd16SPrathyush K 	ctx->powered = true;
1062db43fd16SPrathyush K 	mutex_unlock(&ctx->mixer_mutex);
1063db43fd16SPrathyush K 
1064*af65c804SSean Paul 	pm_runtime_get_sync(ctx->dev);
1065*af65c804SSean Paul 
10660bfb1f8bSSean Paul 	clk_prepare_enable(res->mixer);
1067db43fd16SPrathyush K 	if (ctx->vp_enabled) {
10680bfb1f8bSSean Paul 		clk_prepare_enable(res->vp);
10690bfb1f8bSSean Paul 		clk_prepare_enable(res->sclk_mixer);
1070db43fd16SPrathyush K 	}
1071db43fd16SPrathyush K 
1072db43fd16SPrathyush K 	mixer_reg_write(res, MXR_INT_EN, ctx->int_en);
1073db43fd16SPrathyush K 	mixer_win_reset(ctx);
1074db43fd16SPrathyush K 
1075f041b257SSean Paul 	mixer_window_resume(mgr);
1076db43fd16SPrathyush K }
1077db43fd16SPrathyush K 
1078f041b257SSean Paul static void mixer_poweroff(struct exynos_drm_manager *mgr)
1079db43fd16SPrathyush K {
1080f041b257SSean Paul 	struct mixer_context *ctx = mgr->ctx;
1081db43fd16SPrathyush K 	struct mixer_resources *res = &ctx->mixer_res;
1082db43fd16SPrathyush K 
1083db43fd16SPrathyush K 	mutex_lock(&ctx->mixer_mutex);
1084db43fd16SPrathyush K 	if (!ctx->powered)
1085db43fd16SPrathyush K 		goto out;
1086db43fd16SPrathyush K 	mutex_unlock(&ctx->mixer_mutex);
1087db43fd16SPrathyush K 
1088f041b257SSean Paul 	mixer_window_suspend(mgr);
1089db43fd16SPrathyush K 
1090db43fd16SPrathyush K 	ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
1091db43fd16SPrathyush K 
10920bfb1f8bSSean Paul 	clk_disable_unprepare(res->mixer);
1093db43fd16SPrathyush K 	if (ctx->vp_enabled) {
10940bfb1f8bSSean Paul 		clk_disable_unprepare(res->vp);
10950bfb1f8bSSean Paul 		clk_disable_unprepare(res->sclk_mixer);
1096db43fd16SPrathyush K 	}
1097db43fd16SPrathyush K 
1098*af65c804SSean Paul 	pm_runtime_put_sync(ctx->dev);
1099*af65c804SSean Paul 
1100db43fd16SPrathyush K 	mutex_lock(&ctx->mixer_mutex);
1101db43fd16SPrathyush K 	ctx->powered = false;
1102db43fd16SPrathyush K 
1103db43fd16SPrathyush K out:
1104db43fd16SPrathyush K 	mutex_unlock(&ctx->mixer_mutex);
1105db43fd16SPrathyush K }
1106db43fd16SPrathyush K 
1107f041b257SSean Paul static void mixer_dpms(struct exynos_drm_manager *mgr, int mode)
1108db43fd16SPrathyush K {
1109db43fd16SPrathyush K 	switch (mode) {
1110db43fd16SPrathyush K 	case DRM_MODE_DPMS_ON:
1111*af65c804SSean Paul 		mixer_poweron(mgr);
1112db43fd16SPrathyush K 		break;
1113db43fd16SPrathyush K 	case DRM_MODE_DPMS_STANDBY:
1114db43fd16SPrathyush K 	case DRM_MODE_DPMS_SUSPEND:
1115db43fd16SPrathyush K 	case DRM_MODE_DPMS_OFF:
1116*af65c804SSean Paul 		mixer_poweroff(mgr);
1117db43fd16SPrathyush K 		break;
1118db43fd16SPrathyush K 	default:
1119db43fd16SPrathyush K 		DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
1120db43fd16SPrathyush K 		break;
1121db43fd16SPrathyush K 	}
1122db43fd16SPrathyush K }
1123db43fd16SPrathyush K 
1124f041b257SSean Paul /* Only valid for Mixer version 16.0.33.0 */
1125f041b257SSean Paul int mixer_check_mode(struct drm_display_mode *mode)
1126f041b257SSean Paul {
1127f041b257SSean Paul 	u32 w, h;
1128f041b257SSean Paul 
1129f041b257SSean Paul 	w = mode->hdisplay;
1130f041b257SSean Paul 	h = mode->vdisplay;
1131f041b257SSean Paul 
1132f041b257SSean Paul 	DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n",
1133f041b257SSean Paul 		mode->hdisplay, mode->vdisplay, mode->vrefresh,
1134f041b257SSean Paul 		(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
1135f041b257SSean Paul 
1136f041b257SSean Paul 	if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
1137f041b257SSean Paul 		(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
1138f041b257SSean Paul 		(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
1139f041b257SSean Paul 		return 0;
1140f041b257SSean Paul 
1141f041b257SSean Paul 	return -EINVAL;
1142f041b257SSean Paul }
1143f041b257SSean Paul 
1144f041b257SSean Paul static struct exynos_drm_manager_ops mixer_manager_ops = {
11454551789fSSean Paul 	.initialize		= mixer_initialize,
1146f041b257SSean Paul 	.remove			= mixer_mgr_remove,
1147f041b257SSean Paul 	.dpms			= mixer_dpms,
1148d8408326SSeung-Woo Kim 	.enable_vblank		= mixer_enable_vblank,
1149d8408326SSeung-Woo Kim 	.disable_vblank		= mixer_disable_vblank,
11508137a2e2SPrathyush K 	.wait_for_vblank	= mixer_wait_for_vblank,
1151d8408326SSeung-Woo Kim 	.win_mode_set		= mixer_win_mode_set,
1152d8408326SSeung-Woo Kim 	.win_commit		= mixer_win_commit,
1153d8408326SSeung-Woo Kim 	.win_disable		= mixer_win_disable,
1154f041b257SSean Paul };
11550ea6822fSRahul Sharma 
1156f041b257SSean Paul static struct exynos_drm_manager mixer_manager = {
1157f041b257SSean Paul 	.type			= EXYNOS_DISPLAY_TYPE_HDMI,
1158f041b257SSean Paul 	.ops			= &mixer_manager_ops,
1159d8408326SSeung-Woo Kim };
1160d8408326SSeung-Woo Kim 
1161def5e095SRahul Sharma static struct mixer_drv_data exynos5420_mxr_drv_data = {
1162def5e095SRahul Sharma 	.version = MXR_VER_128_0_0_184,
1163def5e095SRahul Sharma 	.is_vp_enabled = 0,
1164def5e095SRahul Sharma };
1165def5e095SRahul Sharma 
1166cc57caf0SRahul Sharma static struct mixer_drv_data exynos5250_mxr_drv_data = {
1167aaf8b49eSRahul Sharma 	.version = MXR_VER_16_0_33_0,
1168aaf8b49eSRahul Sharma 	.is_vp_enabled = 0,
1169aaf8b49eSRahul Sharma };
1170aaf8b49eSRahul Sharma 
1171cc57caf0SRahul Sharma static struct mixer_drv_data exynos4210_mxr_drv_data = {
11721e123441SRahul Sharma 	.version = MXR_VER_0_0_0_16,
11731b8e5747SRahul Sharma 	.is_vp_enabled = 1,
11741e123441SRahul Sharma };
11751e123441SRahul Sharma 
11761e123441SRahul Sharma static struct platform_device_id mixer_driver_types[] = {
11771e123441SRahul Sharma 	{
11781e123441SRahul Sharma 		.name		= "s5p-mixer",
1179cc57caf0SRahul Sharma 		.driver_data	= (unsigned long)&exynos4210_mxr_drv_data,
11801e123441SRahul Sharma 	}, {
1181aaf8b49eSRahul Sharma 		.name		= "exynos5-mixer",
1182cc57caf0SRahul Sharma 		.driver_data	= (unsigned long)&exynos5250_mxr_drv_data,
1183aaf8b49eSRahul Sharma 	}, {
1184aaf8b49eSRahul Sharma 		/* end node */
1185aaf8b49eSRahul Sharma 	}
1186aaf8b49eSRahul Sharma };
1187aaf8b49eSRahul Sharma 
1188aaf8b49eSRahul Sharma static struct of_device_id mixer_match_types[] = {
1189aaf8b49eSRahul Sharma 	{
1190aaf8b49eSRahul Sharma 		.compatible = "samsung,exynos5-mixer",
1191cc57caf0SRahul Sharma 		.data	= &exynos5250_mxr_drv_data,
1192cc57caf0SRahul Sharma 	}, {
1193cc57caf0SRahul Sharma 		.compatible = "samsung,exynos5250-mixer",
1194cc57caf0SRahul Sharma 		.data	= &exynos5250_mxr_drv_data,
1195aaf8b49eSRahul Sharma 	}, {
1196def5e095SRahul Sharma 		.compatible = "samsung,exynos5420-mixer",
1197def5e095SRahul Sharma 		.data	= &exynos5420_mxr_drv_data,
1198def5e095SRahul Sharma 	}, {
11991e123441SRahul Sharma 		/* end node */
12001e123441SRahul Sharma 	}
12011e123441SRahul Sharma };
12021e123441SRahul Sharma 
120356550d94SGreg Kroah-Hartman static int mixer_probe(struct platform_device *pdev)
1204d8408326SSeung-Woo Kim {
1205d8408326SSeung-Woo Kim 	struct device *dev = &pdev->dev;
1206d8408326SSeung-Woo Kim 	struct mixer_context *ctx;
12071e123441SRahul Sharma 	struct mixer_drv_data *drv;
1208d8408326SSeung-Woo Kim 
1209d8408326SSeung-Woo Kim 	dev_info(dev, "probe start\n");
1210d8408326SSeung-Woo Kim 
1211f041b257SSean Paul 	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1212f041b257SSean Paul 	if (!ctx) {
1213f041b257SSean Paul 		DRM_ERROR("failed to alloc mixer context.\n");
1214d8408326SSeung-Woo Kim 		return -ENOMEM;
1215f041b257SSean Paul 	}
1216d8408326SSeung-Woo Kim 
1217cf8fc4f1SJoonyoung Shim 	mutex_init(&ctx->mixer_mutex);
1218cf8fc4f1SJoonyoung Shim 
1219aaf8b49eSRahul Sharma 	if (dev->of_node) {
1220aaf8b49eSRahul Sharma 		const struct of_device_id *match;
1221e436b09dSSachin Kamat 		match = of_match_node(mixer_match_types, dev->of_node);
12222cdc53b3SRahul Sharma 		drv = (struct mixer_drv_data *)match->data;
1223aaf8b49eSRahul Sharma 	} else {
1224aaf8b49eSRahul Sharma 		drv = (struct mixer_drv_data *)
1225aaf8b49eSRahul Sharma 			platform_get_device_id(pdev)->driver_data;
1226aaf8b49eSRahul Sharma 	}
1227aaf8b49eSRahul Sharma 
12284551789fSSean Paul 	ctx->pdev = pdev;
1229d873ab99SSeung-Woo Kim 	ctx->dev = dev;
12301b8e5747SRahul Sharma 	ctx->vp_enabled = drv->is_vp_enabled;
12311e123441SRahul Sharma 	ctx->mxr_ver = drv->version;
123257ed0f7bSDaniel Vetter 	init_waitqueue_head(&ctx->wait_vsync_queue);
12336e95d5e6SPrathyush K 	atomic_set(&ctx->wait_vsync_event, 0);
1234d8408326SSeung-Woo Kim 
1235f041b257SSean Paul 	mixer_manager.ctx = ctx;
1236f041b257SSean Paul 	platform_set_drvdata(pdev, &mixer_manager);
1237f041b257SSean Paul 	exynos_drm_manager_register(&mixer_manager);
1238d8408326SSeung-Woo Kim 
1239cf8fc4f1SJoonyoung Shim 	pm_runtime_enable(dev);
1240d8408326SSeung-Woo Kim 
1241d8408326SSeung-Woo Kim 	return 0;
1242d8408326SSeung-Woo Kim }
1243d8408326SSeung-Woo Kim 
1244d8408326SSeung-Woo Kim static int mixer_remove(struct platform_device *pdev)
1245d8408326SSeung-Woo Kim {
12469416dfa7SSachin Kamat 	dev_info(&pdev->dev, "remove successful\n");
1247d8408326SSeung-Woo Kim 
1248cf8fc4f1SJoonyoung Shim 	pm_runtime_disable(&pdev->dev);
1249cf8fc4f1SJoonyoung Shim 
1250d8408326SSeung-Woo Kim 	return 0;
1251d8408326SSeung-Woo Kim }
1252d8408326SSeung-Woo Kim 
1253d8408326SSeung-Woo Kim struct platform_driver mixer_driver = {
1254d8408326SSeung-Woo Kim 	.driver = {
1255aaf8b49eSRahul Sharma 		.name = "exynos-mixer",
1256d8408326SSeung-Woo Kim 		.owner = THIS_MODULE,
1257aaf8b49eSRahul Sharma 		.of_match_table = mixer_match_types,
1258d8408326SSeung-Woo Kim 	},
1259d8408326SSeung-Woo Kim 	.probe = mixer_probe,
126056550d94SGreg Kroah-Hartman 	.remove = mixer_remove,
12611e123441SRahul Sharma 	.id_table	= mixer_driver_types,
1262d8408326SSeung-Woo Kim };
1263