summaryrefslogtreecommitdiff
path: root/drivers/gpu/imx/dpu/dpu-tcon.c
blob: a0a94ce0f47d4bab9b726fd255ebd42d046843d0 (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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
 * Copyright (C) 2016 Freescale Semiconductor, Inc.
 * Copyright 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/io.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <video/dpu.h>
#include "dpu-prv.h"

#define SSQCNTS			0
#define SSQCYCLE		0x8
#define SWRESET			0xC
#define TCON_CTRL		0x10
#define BYPASS			BIT(3)
#define RSDSINVCTRL		0x14
#define MAPBIT3_0		0x18
#define MAPBIT7_4		0x1C
#define MAPBIT11_8		0x20
#define MAPBIT15_12		0x24
#define MAPBIT19_16		0x28
#define MAPBIT23_20		0x2C
#define MAPBIT27_24		0x30
#define MAPBIT31_28		0x34
#define MAPBIT34_32		0x38
#define MAPBIT3_0_DUAL		0x3C
#define MAPBIT7_4_DUAL		0x40
#define MAPBIT11_8_DUAL		0x44
#define MAPBIT15_12_DUAL	0x48
#define MAPBIT19_16_DUAL	0x4C
#define MAPBIT23_20_DUAL	0x50
#define MAPBIT27_24_DUAL	0x54
#define MAPBIT31_28_DUAL	0x58
#define MAPBIT34_32_DUAL	0x5C
#define SPGPOSON(n)		(0x60 + (n) * 16)
#define X(n)			(((n) & 0x7FFF) << 16)
#define Y(n)			((n) & 0x7FFF)
#define SPGMASKON(n)		(0x64 + (n) * 16)
#define SPGPOSOFF(n)		(0x68 + (n) * 16)
#define SPGMASKOFF(n)		(0x6C + (n) * 16)
#define SMXSIGS(n)		(0x120 + (n) * 8)
#define SMXFCTTABLE(n)		(0x124 + (n) * 8)
#define RESET_OVER_UNFERFLOW	0x180
#define DUAL_DEBUG		0x184

struct dpu_tcon {
	void __iomem *base;
	struct mutex mutex;
	int id;
	bool inuse;
	struct dpu_soc *dpu;
};

static inline u32 dpu_tcon_read(struct dpu_tcon *tcon, unsigned int offset)
{
	return readl(tcon->base + offset);
}

static inline void dpu_tcon_write(struct dpu_tcon *tcon, u32 value,
				  unsigned int offset)
{
	writel(value, tcon->base + offset);
}

int tcon_set_fmt(struct dpu_tcon *tcon, u32 bus_format)
{
	mutex_lock(&tcon->mutex);
	switch (bus_format) {
	case MEDIA_BUS_FMT_RGB888_1X24:
		dpu_tcon_write(tcon, 0x19181716, MAPBIT3_0);
		dpu_tcon_write(tcon, 0x1d1c1b1a, MAPBIT7_4);
		dpu_tcon_write(tcon, 0x0f0e0d0c, MAPBIT11_8);
		dpu_tcon_write(tcon, 0x13121110, MAPBIT15_12);
		dpu_tcon_write(tcon, 0x05040302, MAPBIT19_16);
		dpu_tcon_write(tcon, 0x09080706, MAPBIT23_20);
		break;
	case MEDIA_BUS_FMT_RGB101010_1X30:
	case MEDIA_BUS_FMT_RGB888_1X30_PADLO:
	case MEDIA_BUS_FMT_RGB666_1X30_PADLO:
		dpu_tcon_write(tcon, 0x17161514, MAPBIT3_0);
		dpu_tcon_write(tcon, 0x1b1a1918, MAPBIT7_4);
		dpu_tcon_write(tcon, 0x0b0a1d1c, MAPBIT11_8);
		dpu_tcon_write(tcon, 0x0f0e0d0c, MAPBIT15_12);
		dpu_tcon_write(tcon, 0x13121110, MAPBIT19_16);
		dpu_tcon_write(tcon, 0x03020100, MAPBIT23_20);
		dpu_tcon_write(tcon, 0x07060504, MAPBIT27_24);
		dpu_tcon_write(tcon, 0x00000908, MAPBIT31_28);
		break;
	default:
		mutex_unlock(&tcon->mutex);
		return -EINVAL;
	}
	mutex_unlock(&tcon->mutex);

	return 0;
}
EXPORT_SYMBOL_GPL(tcon_set_fmt);

/* This function is used to workaround TKT320590 which is related to DPR/PRG. */
void tcon_set_operation_mode(struct dpu_tcon *tcon)
{
	u32 val;

	mutex_lock(&tcon->mutex);
	val = dpu_tcon_read(tcon, TCON_CTRL);
	val &= ~BYPASS;
	dpu_tcon_write(tcon, val, TCON_CTRL);
	mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(tcon_set_operation_mode);

void tcon_cfg_videomode(struct dpu_tcon *tcon, struct drm_display_mode *m)
{
	u32 val;

	mutex_lock(&tcon->mutex);
	/*
	 * TKT320590:
	 * Turn TCON into operation mode later after the first dumb frame is
	 * generated by DPU.  This makes DPR/PRG be able to evade the frame.
	 */
	val = dpu_tcon_read(tcon, TCON_CTRL);
	val |= BYPASS;
	dpu_tcon_write(tcon, val, TCON_CTRL);

	/* dsp_control[0]: hsync */
	dpu_tcon_write(tcon, X(m->hsync_start), SPGPOSON(0));
	dpu_tcon_write(tcon, 0xffff, SPGMASKON(0));

	dpu_tcon_write(tcon, X(m->hsync_end), SPGPOSOFF(0));
	dpu_tcon_write(tcon, 0xffff, SPGMASKOFF(0));

	dpu_tcon_write(tcon, 0x2, SMXSIGS(0));
	dpu_tcon_write(tcon, 0x1, SMXFCTTABLE(0));

	/* dsp_control[1]: vsync */
	dpu_tcon_write(tcon, X(m->hsync_start) | Y(m->vsync_start - 1),
								SPGPOSON(1));
	dpu_tcon_write(tcon, 0x0, SPGMASKON(1));

	dpu_tcon_write(tcon, X(m->hsync_start) | Y(m->vsync_end - 1),
								SPGPOSOFF(1));
	dpu_tcon_write(tcon, 0x0, SPGMASKOFF(1));

	dpu_tcon_write(tcon, 0x3, SMXSIGS(1));
	dpu_tcon_write(tcon, 0x1, SMXFCTTABLE(1));

	/* dsp_control[2]: data enable */
	/* horizontal */
	dpu_tcon_write(tcon, 0x0, SPGPOSON(2));
	dpu_tcon_write(tcon, 0xffff, SPGMASKON(2));

	dpu_tcon_write(tcon, X(m->hdisplay), SPGPOSOFF(2));
	dpu_tcon_write(tcon, 0xffff, SPGMASKOFF(2));

	/* vertical */
	dpu_tcon_write(tcon, 0x0, SPGPOSON(3));
	dpu_tcon_write(tcon, 0x7fff0000, SPGMASKON(3));

	dpu_tcon_write(tcon, Y(m->vdisplay), SPGPOSOFF(3));
	dpu_tcon_write(tcon, 0x7fff0000, SPGMASKOFF(3));

	dpu_tcon_write(tcon, 0x2c, SMXSIGS(2));
	dpu_tcon_write(tcon, 0x8, SMXFCTTABLE(2));

	/* dsp_control[3]: kachuck */
	dpu_tcon_write(tcon, X(0xa) | Y(m->vdisplay), SPGPOSON(4));
	dpu_tcon_write(tcon, 0x0, SPGMASKON(4));

	dpu_tcon_write(tcon, X(0x2a) | Y(m->vdisplay), SPGPOSOFF(4));
	dpu_tcon_write(tcon, 0x0, SPGMASKOFF(4));

	dpu_tcon_write(tcon, 0x6, SMXSIGS(3));
	dpu_tcon_write(tcon, 0x2, SMXFCTTABLE(3));
	mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(tcon_cfg_videomode);

struct dpu_tcon *dpu_tcon_get(struct dpu_soc *dpu, int id)
{
	struct dpu_tcon *tcon;
	int i;

	for (i = 0; i < ARRAY_SIZE(tcon_ids); i++)
		if (tcon_ids[i] == id)
			break;

	if (i == ARRAY_SIZE(tcon_ids))
		return ERR_PTR(-EINVAL);

	tcon = dpu->tcon_priv[i];

	mutex_lock(&tcon->mutex);

	if (tcon->inuse) {
		tcon = ERR_PTR(-EBUSY);
		goto out;
	}

	tcon->inuse = true;
out:
	mutex_unlock(&tcon->mutex);

	return tcon;
}
EXPORT_SYMBOL_GPL(dpu_tcon_get);

void dpu_tcon_put(struct dpu_tcon *tcon)
{
	mutex_lock(&tcon->mutex);

	tcon->inuse = false;

	mutex_unlock(&tcon->mutex);
}
EXPORT_SYMBOL_GPL(dpu_tcon_put);

void _dpu_tcon_init(struct dpu_soc *dpu, unsigned int id)
{
}

int dpu_tcon_init(struct dpu_soc *dpu, unsigned int id,
			unsigned long unused, unsigned long base)
{
	struct dpu_tcon *tcon;

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

	dpu->tcon_priv[id] = tcon;

	tcon->base = devm_ioremap(dpu->dev, base, SZ_512);
	if (!tcon->base)
		return -ENOMEM;

	tcon->dpu = dpu;
	mutex_init(&tcon->mutex);

	return 0;
}