summaryrefslogtreecommitdiff
path: root/drivers/gpu/imx/dcss/dcss-ss.c
blob: f44fb602e140426304ad9e76d9169155564ac650 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * Copyright (C) 2017 NXP
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

#include <linux/device.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <video/videomode.h>
#include <drm/drm_fourcc.h>

#include <video/imx-dcss.h>
#include "dcss-prv.h"

#define USE_CTXLD

#define DCSS_SS_SYS_CTRL			0x00
#define   RUN_EN				BIT(0)
#define DCSS_SS_DISPLAY				0x10
#define   LRC_X_POS				0
#define   LRC_X_MASK				GENMASK(12, 0)
#define   LRC_Y_POS				16
#define   LRC_Y_MASK				GENMASK(28, 16)
#define DCSS_SS_HSYNC				0x20
#define DCSS_SS_VSYNC				0x30
#define   SYNC_START_POS			0
#define   SYNC_START_MASK			GENMASK(12, 0)
#define   SYNC_END_POS				16
#define   SYNC_END_MASK				GENMASK(28, 16)
#define   SYNC_POL				BIT(31)
#define DCSS_SS_DE_ULC				0x40
#define   ULC_X_POS				0
#define   ULC_X_MASK				GENMASK(12, 0)
#define   ULC_Y_POS				16
#define   ULC_Y_MASK				GENMASK(28, 16)
#define   ULC_POL				BIT(31)
#define DCSS_SS_DE_LRC				0x50
#define DCSS_SS_MODE				0x60
#define   PIPE_MODE_POS				0
#define   PIPE_MODE_MASK			GENMASK(1, 0)
#define DCSS_SS_COEFF				0x70
#define   HORIZ_A_POS				0
#define   HORIZ_A_MASK				GENMASK(3, 0)
#define   HORIZ_B_POS				4
#define   HORIZ_B_MASK				GENMASK(7, 4)
#define   HORIZ_C_POS				8
#define   HORIZ_C_MASK				GENMASK(11, 8)
#define   HORIZ_H_NORM_POS			12
#define   HORIZ_H_NORM_MASK			GENMASK(14, 12)
#define   VERT_A_POS				16
#define   VERT_A_MASK				GENMASK(19, 16)
#define   VERT_B_POS				20
#define   VERT_B_MASK				GENMASK(23, 20)
#define   VERT_C_POS				24
#define   VERT_C_MASK				GENMASK(27, 24)
#define   VERT_H_NORM_POS			28
#define   VERT_H_NORM_MASK			GENMASK(30, 28)
#define DCSS_SS_CLIP_CB				0x80
#define DCSS_SS_CLIP_CR				0x90
#define   CLIP_MIN_POS				0
#define   CLIP_MIN_MASK				GENMASK(9, 0)
#define   CLIP_MAX_POS				0
#define   CLIP_MAX_MASK				GENMASK(23, 16)
#define DCSS_SS_INTER_MODE			0xA0
#define   INT_EN				BIT(0)
#define   VSYNC_SHIFT				BIT(1)

static struct dcss_debug_reg ss_debug_reg[] = {
	DCSS_DBG_REG(DCSS_SS_SYS_CTRL),
	DCSS_DBG_REG(DCSS_SS_DISPLAY),
	DCSS_DBG_REG(DCSS_SS_HSYNC),
	DCSS_DBG_REG(DCSS_SS_VSYNC),
	DCSS_DBG_REG(DCSS_SS_DE_ULC),
	DCSS_DBG_REG(DCSS_SS_DE_LRC),
	DCSS_DBG_REG(DCSS_SS_MODE),
	DCSS_DBG_REG(DCSS_SS_COEFF),
	DCSS_DBG_REG(DCSS_SS_CLIP_CB),
	DCSS_DBG_REG(DCSS_SS_CLIP_CR),
	DCSS_DBG_REG(DCSS_SS_INTER_MODE),
};

struct dcss_ss_priv {
	struct dcss_soc *dcss;
	void __iomem *base_reg;
	u32 base_ofs;

	u32 ctx_id;

	bool in_use;
};

static void dcss_ss_write(struct dcss_ss_priv *ss, u32 val, u32 ofs)
{
	if (!ss->in_use)
		dcss_writel(val, ss->base_reg + ofs);
#if defined(USE_CTXLD)
	dcss_ctxld_write(ss->dcss, ss->ctx_id, val,
			 ss->base_ofs + ofs);
#endif
}

#ifdef CONFIG_DEBUG_FS
void dcss_ss_dump_regs(struct seq_file *s, void *data)
{
	struct dcss_soc *dcss = data;
	int j;

	seq_puts(s, ">> Dumping SUBSAM:\n");
	for (j = 0; j < ARRAY_SIZE(ss_debug_reg); j++)
		seq_printf(s, "%-35s(0x%04x) -> 0x%08x\n",
			   ss_debug_reg[j].name,
			   ss_debug_reg[j].ofs,
			   dcss_readl(dcss->ss_priv->base_reg +
				      ss_debug_reg[j].ofs));
}
#endif

int dcss_ss_init(struct dcss_soc *dcss, unsigned long ss_base)
{
	struct dcss_ss_priv *ss;

	ss = devm_kzalloc(dcss->dev, sizeof(*ss), GFP_KERNEL);
	if (!ss)
		return -ENOMEM;

	dcss->ss_priv = ss;
	ss->dcss = dcss;

	ss->base_reg = devm_ioremap(dcss->dev, ss_base, SZ_4K);
	if (!ss->base_reg) {
		dev_err(dcss->dev, "ss: unable to remap ss base\n");
		return -ENOMEM;
	}

	ss->base_ofs = ss_base;

#if defined(USE_CTXLD)
	ss->ctx_id = CTX_SB_HP;
#endif

	return 0;
}

void dcss_ss_exit(struct dcss_soc *dcss)
{
	dcss_writel(0, dcss->ss_priv->base_reg + DCSS_SS_SYS_CTRL);
}

void dcss_ss_subsam_set(struct dcss_soc *dcss, u32 pix_format)
{
	if (pix_format == DRM_FORMAT_P010) {
		dcss_ss_write(dcss->ss_priv, 0x21612161, DCSS_SS_COEFF);
		dcss_ss_write(dcss->ss_priv, 2, DCSS_SS_MODE);
		dcss_ss_write(dcss->ss_priv, 0x03c00040, DCSS_SS_CLIP_CB);
		dcss_ss_write(dcss->ss_priv, 0x03c00040, DCSS_SS_CLIP_CR);

		return;
	}

	dcss_ss_write(dcss->ss_priv, 0x41614161, DCSS_SS_COEFF);
	dcss_ss_write(dcss->ss_priv, 0, DCSS_SS_MODE);
	dcss_ss_write(dcss->ss_priv, 0x03ff0000, DCSS_SS_CLIP_CB);
	dcss_ss_write(dcss->ss_priv, 0x03ff0000, DCSS_SS_CLIP_CR);
}

void dcss_ss_sync_set(struct dcss_soc *dcss, struct videomode *vm,
		      bool phsync, bool pvsync)
{
	struct dcss_ss_priv *ss = dcss->ss_priv;
	u16 lrc_x, lrc_y;
	u16 hsync_start, hsync_end;
	u16 vsync_start, vsync_end;
	u16 de_ulc_x, de_ulc_y;
	u16 de_lrc_x, de_lrc_y;

	lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
		vm->hactive - 1;
	lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
		vm->vactive - 1;

	dcss_ss_write(ss, (lrc_y << LRC_Y_POS) | lrc_x, DCSS_SS_DISPLAY);

	hsync_start = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
		      vm->hactive - 1;
	hsync_end = vm->hsync_len - 1;

	dcss_ss_write(ss, (phsync ? SYNC_POL : 0) |
		      (hsync_end << SYNC_END_POS) | hsync_start,
		      DCSS_SS_HSYNC);

	vsync_start = vm->vfront_porch - 1;
	vsync_end = vm->vfront_porch + vm->vsync_len - 1;

	dcss_ss_write(ss, (pvsync ? SYNC_POL : 0) |
		      (vsync_end << SYNC_END_POS) | vsync_start, DCSS_SS_VSYNC);

	de_ulc_x = vm->hsync_len + vm->hback_porch - 1;
	de_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch;

	dcss_ss_write(ss, SYNC_POL | (de_ulc_y << ULC_Y_POS) | de_ulc_x,
		      DCSS_SS_DE_ULC);

	de_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
	de_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
		   vm->vactive - 1;

	dcss_ss_write(ss, (de_lrc_y << LRC_Y_POS) | de_lrc_x, DCSS_SS_DE_LRC);
}
EXPORT_SYMBOL(dcss_ss_sync_set);

void dcss_ss_enable(struct dcss_soc *dcss, bool en)
{
	struct dcss_ss_priv *ss = dcss->ss_priv;

	dcss_ss_write(ss, en ? RUN_EN : 0, DCSS_SS_SYS_CTRL);
}
EXPORT_SYMBOL(dcss_ss_enable);