aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Gaignard <benjamin.gaignard@linaro.org>2014-07-30 12:20:56 -0400
committerBenjamin Gaignard <benjamin.gaignard@linaro.org>2014-07-30 12:20:56 -0400
commitf2cb3148642533f6c162ce61806b25b6c622ab90 (patch)
tree22eaf0fa3697515b11049b213290104faa5bb736
parent30ebb9088c50181e0f8a2013f7d7579aa3480833 (diff)
drm: sti: add VTG driver
Video Time Generator drivers are used to synchronize the compositor and tvout hardware IPs by providing line count, sample count, synchronization signals (HSYNC, VSYNC) and top and bottom fields indication. VTG are used by pair for each data path (main or auxiliary) one for master and one for slave. Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Reviewed-by: Rob Clark <robdclark@gmail.com>
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/sti/Kconfig5
-rw-r--r--drivers/gpu/drm/sti/Makefile2
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.c366
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.h28
6 files changed, 404 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f5120046ff80..31894c8c1773 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -201,3 +201,5 @@ source "drivers/gpu/drm/msm/Kconfig"
201source "drivers/gpu/drm/tegra/Kconfig" 201source "drivers/gpu/drm/tegra/Kconfig"
202 202
203source "drivers/gpu/drm/panel/Kconfig" 203source "drivers/gpu/drm/panel/Kconfig"
204
205source "drivers/gpu/drm/sti/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 61d9e9c6bc10..60eb5d425df2 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_DRM_QXL) += qxl/
64obj-$(CONFIG_DRM_BOCHS) += bochs/ 64obj-$(CONFIG_DRM_BOCHS) += bochs/
65obj-$(CONFIG_DRM_MSM) += msm/ 65obj-$(CONFIG_DRM_MSM) += msm/
66obj-$(CONFIG_DRM_TEGRA) += tegra/ 66obj-$(CONFIG_DRM_TEGRA) += tegra/
67obj-$(CONFIG_DRM_STI) += sti/
67obj-y += i2c/ 68obj-y += i2c/
68obj-y += panel/ 69obj-y += panel/
69obj-y += bridge/ 70obj-y += bridge/
diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
new file mode 100644
index 000000000000..a18df02dc95a
--- /dev/null
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -0,0 +1,5 @@
1config DRM_STI
2 tristate "DRM Support for STMicroelectronics SoC stiH41x Series"
3 depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
4 help
5 Choose this option to enable DRM on STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
new file mode 100644
index 000000000000..1759c20ee33e
--- /dev/null
+++ b/drivers/gpu/drm/sti/Makefile
@@ -0,0 +1,2 @@
1obj-$(CONFIG_DRM_STI) = \
2 sti_vtg.o
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c
new file mode 100644
index 000000000000..740d6e347a62
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.c
@@ -0,0 +1,366 @@
1/*
2 * Copyright (C) STMicroelectronics SA 2014
3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
4 * Fabien Dessenne <fabien.dessenne@st.com>
5 * Vincent Abriou <vincent.abriou@st.com>
6 * for STMicroelectronics.
7 * License terms: GNU General Public License (GPL), version 2
8 */
9
10#include <linux/module.h>
11#include <linux/notifier.h>
12#include <linux/platform_device.h>
13
14#include <drm/drmP.h>
15
16#include "sti_vtg.h"
17
18#define VTG_TYPE_MASTER 0
19#define VTG_TYPE_SLAVE_BY_EXT0 1
20
21/* registers offset */
22#define VTG_MODE 0x0000
23#define VTG_CLKLN 0x0008
24#define VTG_HLFLN 0x000C
25#define VTG_DRST_AUTOC 0x0010
26#define VTG_VID_TFO 0x0040
27#define VTG_VID_TFS 0x0044
28#define VTG_VID_BFO 0x0048
29#define VTG_VID_BFS 0x004C
30
31#define VTG_HOST_ITS 0x0078
32#define VTG_HOST_ITS_BCLR 0x007C
33#define VTG_HOST_ITM_BCLR 0x0088
34#define VTG_HOST_ITM_BSET 0x008C
35
36#define VTG_H_HD_1 0x00C0
37#define VTG_TOP_V_VD_1 0x00C4
38#define VTG_BOT_V_VD_1 0x00C8
39#define VTG_TOP_V_HD_1 0x00CC
40#define VTG_BOT_V_HD_1 0x00D0
41
42#define VTG_H_HD_2 0x00E0
43#define VTG_TOP_V_VD_2 0x00E4
44#define VTG_BOT_V_VD_2 0x00E8
45#define VTG_TOP_V_HD_2 0x00EC
46#define VTG_BOT_V_HD_2 0x00F0
47
48#define VTG_H_HD_3 0x0100
49#define VTG_TOP_V_VD_3 0x0104
50#define VTG_BOT_V_VD_3 0x0108
51#define VTG_TOP_V_HD_3 0x010C
52#define VTG_BOT_V_HD_3 0x0110
53
54#define VTG_IRQ_BOTTOM BIT(0)
55#define VTG_IRQ_TOP BIT(1)
56#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM)
57
58/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */
59#define AWG_DELAY_HD (-9)
60#define AWG_DELAY_ED (-8)
61#define AWG_DELAY_SD (-7)
62
63LIST_HEAD(vtg_lookup);
64
65/**
66 * STI VTG structure
67 *
68 * @dev: pointer to device driver
69 * @data: data associated to the device
70 * @irq: VTG irq
71 * @type: VTG type (main or aux)
72 * @notifier_list: notifier callback
73 * @crtc_id: the crtc id for vblank event
74 * @slave: slave vtg
75 * @link: List node to link the structure in lookup list
76 */
77struct sti_vtg {
78 struct device *dev;
79 struct device_node *np;
80 void __iomem *regs;
81 int irq;
82 u32 irq_status;
83 struct raw_notifier_head notifier_list;
84 int crtc_id;
85 struct sti_vtg *slave;
86 struct list_head link;
87};
88
89static void vtg_register(struct sti_vtg *vtg)
90{
91 list_add_tail(&vtg->link, &vtg_lookup);
92}
93
94struct sti_vtg *of_vtg_find(struct device_node *np)
95{
96 struct sti_vtg *vtg;
97
98 list_for_each_entry(vtg, &vtg_lookup, link) {
99 if (vtg->np == np)
100 return vtg;
101 }
102 return NULL;
103}
104EXPORT_SYMBOL(of_vtg_find);
105
106static void vtg_reset(struct sti_vtg *vtg)
107{
108 /* reset slave and then master */
109 if (vtg->slave)
110 vtg_reset(vtg->slave);
111
112 writel(1, vtg->regs + VTG_DRST_AUTOC);
113}
114
115static void vtg_set_mode(struct sti_vtg *vtg,
116 int type, const struct drm_display_mode *mode)
117{
118 u32 tmp;
119
120 if (vtg->slave)
121 vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode);
122
123 writel(mode->htotal, vtg->regs + VTG_CLKLN);
124 writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN);
125
126 tmp = (mode->vtotal - mode->vsync_start + 1) << 16;
127 tmp |= mode->htotal - mode->hsync_start;
128 writel(tmp, vtg->regs + VTG_VID_TFO);
129 writel(tmp, vtg->regs + VTG_VID_BFO);
130
131 tmp = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16;
132 tmp |= mode->hdisplay + mode->htotal - mode->hsync_start;
133 writel(tmp, vtg->regs + VTG_VID_TFS);
134 writel(tmp, vtg->regs + VTG_VID_BFS);
135
136 /* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */
137 tmp = (mode->hsync_end - mode->hsync_start) << 16;
138 writel(tmp, vtg->regs + VTG_H_HD_1);
139 writel(tmp, vtg->regs + VTG_H_HD_2);
140
141 tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
142 tmp |= 1;
143 writel(tmp, vtg->regs + VTG_TOP_V_VD_1);
144 writel(tmp, vtg->regs + VTG_BOT_V_VD_1);
145 writel(0, vtg->regs + VTG_TOP_V_HD_1);
146 writel(0, vtg->regs + VTG_BOT_V_HD_1);
147
148 /* prepare VTG set 2 for for HD DCS */
149 writel(tmp, vtg->regs + VTG_TOP_V_VD_2);
150 writel(tmp, vtg->regs + VTG_BOT_V_VD_2);
151 writel(0, vtg->regs + VTG_TOP_V_HD_2);
152 writel(0, vtg->regs + VTG_BOT_V_HD_2);
153
154 /* prepare VTG set 3 for HD Analog in HD mode */
155 tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16;
156 tmp |= mode->htotal + AWG_DELAY_HD;
157 writel(tmp, vtg->regs + VTG_H_HD_3);
158
159 tmp = (mode->vsync_end - mode->vsync_start) << 16;
160 tmp |= mode->vtotal;
161 writel(tmp, vtg->regs + VTG_TOP_V_VD_3);
162 writel(tmp, vtg->regs + VTG_BOT_V_VD_3);
163
164 tmp = (mode->htotal + AWG_DELAY_HD) << 16;
165 tmp |= mode->htotal + AWG_DELAY_HD;
166 writel(tmp, vtg->regs + VTG_TOP_V_HD_3);
167 writel(tmp, vtg->regs + VTG_BOT_V_HD_3);
168
169 /* mode */
170 writel(type, vtg->regs + VTG_MODE);
171}
172
173static void vtg_enable_irq(struct sti_vtg *vtg)
174{
175 /* clear interrupt status and mask */
176 writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR);
177 writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR);
178 writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET);
179}
180
181void sti_vtg_set_config(struct sti_vtg *vtg,
182 const struct drm_display_mode *mode)
183{
184 /* write configuration */
185 vtg_set_mode(vtg, VTG_TYPE_MASTER, mode);
186
187 vtg_reset(vtg);
188
189 /* enable irq for the vtg vblank synchro */
190 if (vtg->slave)
191 vtg_enable_irq(vtg->slave);
192 else
193 vtg_enable_irq(vtg);
194}
195EXPORT_SYMBOL(sti_vtg_set_config);
196
197/**
198 * sti_vtg_get_line_number
199 *
200 * @mode: display mode to be used
201 * @y: line
202 *
203 * Return the line number according to the display mode taking
204 * into account the Sync and Back Porch information.
205 * Video frame line numbers start at 1, y starts at 0.
206 * In interlaced modes the start line is the field line number of the odd
207 * field, but y is still defined as a progressive frame.
208 */
209u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)
210{
211 u32 start_line = mode.vtotal - mode.vsync_start + 1;
212
213 if (mode.flags & DRM_MODE_FLAG_INTERLACE)
214 start_line *= 2;
215
216 return start_line + y;
217}
218EXPORT_SYMBOL(sti_vtg_get_line_number);
219
220/**
221 * sti_vtg_get_pixel_number
222 *
223 * @mode: display mode to be used
224 * @x: row
225 *
226 * Return the pixel number according to the display mode taking
227 * into account the Sync and Back Porch information.
228 * Pixels are counted from 0.
229 */
230u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)
231{
232 return mode.htotal - mode.hsync_start + x;
233}
234EXPORT_SYMBOL(sti_vtg_get_pixel_number);
235
236int sti_vtg_register_client(struct sti_vtg *vtg,
237 struct notifier_block *nb, int crtc_id)
238{
239 if (vtg->slave)
240 return sti_vtg_register_client(vtg->slave, nb, crtc_id);
241
242 vtg->crtc_id = crtc_id;
243 return raw_notifier_chain_register(&vtg->notifier_list, nb);
244}
245EXPORT_SYMBOL(sti_vtg_register_client);
246
247int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb)
248{
249 if (vtg->slave)
250 return sti_vtg_unregister_client(vtg->slave, nb);
251
252 return raw_notifier_chain_unregister(&vtg->notifier_list, nb);
253}
254EXPORT_SYMBOL(sti_vtg_unregister_client);
255
256static irqreturn_t vtg_irq_thread(int irq, void *arg)
257{
258 struct sti_vtg *vtg = arg;
259 u32 event;
260
261 event = (vtg->irq_status & VTG_IRQ_TOP) ?
262 VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT;
263
264 raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id);
265
266 return IRQ_HANDLED;
267}
268
269static irqreturn_t vtg_irq(int irq, void *arg)
270{
271 struct sti_vtg *vtg = arg;
272
273 vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS);
274
275 writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR);
276
277 /* force sync bus write */
278 readl(vtg->regs + VTG_HOST_ITS);
279
280 return IRQ_WAKE_THREAD;
281}
282
283static int vtg_probe(struct platform_device *pdev)
284{
285 struct device *dev = &pdev->dev;
286 struct device_node *np;
287 struct sti_vtg *vtg;
288 struct resource *res;
289 char irq_name[32];
290 int ret;
291
292 vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);
293 if (!vtg)
294 return -ENOMEM;
295
296 vtg->dev = dev;
297 vtg->np = pdev->dev.of_node;
298
299 /* Get Memory ressources */
300 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
301 if (!res) {
302 DRM_ERROR("Get memory resource failed\n");
303 return -ENOMEM;
304 }
305 vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
306
307 np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0);
308 if (np) {
309 vtg->slave = of_vtg_find(np);
310
311 if (!vtg->slave)
312 return -EPROBE_DEFER;
313 } else {
314 vtg->irq = platform_get_irq(pdev, 0);
315 if (IS_ERR_VALUE(vtg->irq)) {
316 DRM_ERROR("Failed to get VTG interrupt\n");
317 return vtg->irq;
318 }
319
320 snprintf(irq_name, sizeof(irq_name), "vsync-%s",
321 dev_name(vtg->dev));
322
323 RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);
324
325 ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq,
326 vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg);
327 if (IS_ERR_VALUE(ret)) {
328 DRM_ERROR("Failed to register VTG interrupt\n");
329 return ret;
330 }
331 }
332
333 vtg_register(vtg);
334 platform_set_drvdata(pdev, vtg);
335
336 DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev));
337
338 return 0;
339}
340
341static int vtg_remove(struct platform_device *pdev)
342{
343 return 0;
344}
345
346static const struct of_device_id vtg_of_match[] = {
347 { .compatible = "st,vtg", },
348 { /* sentinel */ }
349};
350MODULE_DEVICE_TABLE(of, vtg_of_match);
351
352struct platform_driver sti_vtg_driver = {
353 .driver = {
354 .name = "sti-vtg",
355 .owner = THIS_MODULE,
356 .of_match_table = vtg_of_match,
357 },
358 .probe = vtg_probe,
359 .remove = vtg_remove,
360};
361
362module_platform_driver(sti_vtg_driver);
363
364MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
365MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
366MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h
new file mode 100644
index 000000000000..e84d23f1f57f
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.h
@@ -0,0 +1,28 @@
1/*
2 * Copyright (C) STMicroelectronics SA 2014
3 * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
4 * License terms: GNU General Public License (GPL), version 2
5 */
6
7#ifndef _STI_VTG_H_
8#define _STI_VTG_H_
9
10#define VTG_TOP_FIELD_EVENT 1
11#define VTG_BOTTOM_FIELD_EVENT 2
12
13struct sti_vtg;
14struct drm_display_mode;
15struct notifier_block;
16
17struct sti_vtg *of_vtg_find(struct device_node *np);
18void sti_vtg_set_config(struct sti_vtg *vtg,
19 const struct drm_display_mode *mode);
20int sti_vtg_register_client(struct sti_vtg *vtg,
21 struct notifier_block *nb, int crtc_id);
22int sti_vtg_unregister_client(struct sti_vtg *vtg,
23 struct notifier_block *nb);
24
25u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y);
26u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x);
27
28#endif