2 * Video Timing Controller support for Xilinx DRM KMS
4 * Copyright (C) 2013 Xilinx, Inc.
6 * Author: Hyun Woo Kwon <hyunk@xilinx.com>
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
20 #include <linux/device.h>
21 #include <linux/err.h>
22 #include <linux/interrupt.h>
24 #include <linux/of_address.h>
25 #include <linux/of_irq.h>
26 #include <linux/slab.h>
28 #include <video/videomode.h>
30 #include "xilinx_drm_drv.h"
31 #include "xilinx_vtc.h"
33 /* register offsets */
34 #define VTC_CTL 0x000 /* control */
35 #define VTC_STATS 0x004 /* status */
36 #define VTC_ERROR 0x008 /* error */
38 #define VTC_GASIZE 0x060 /* generator active size */
39 #define VTC_GPOL 0x06c /* generator polarity */
40 #define VTC_GHSIZE 0x070 /* generator frame horizontal size */
41 #define VTC_GVSIZE 0x074 /* generator frame vertical size */
42 #define VTC_GHSYNC 0x078 /* generator horizontal sync */
43 #define VTC_GVBHOFF 0x07c /* generator vblank horizontal offset */
44 #define VTC_GVSYNC 0x080 /* generator vertical sync */
45 #define VTC_GVSHOFF 0x084 /* generator vsync horizontal offset */
47 #define VTC_RESET 0x000 /* reset register */
48 #define VTC_ISR 0x004 /* interrupt status register */
49 #define VTC_IER 0x00c /* interrupt enable register */
51 /* control register bit */
52 #define VTC_CTL_FIP (1 << 6) /* field id output polarity */
53 #define VTC_CTL_ACP (1 << 5) /* active chroma output polarity */
54 #define VTC_CTL_AVP (1 << 4) /* active video output polarity */
55 #define VTC_CTL_HSP (1 << 3) /* hori sync output polarity */
56 #define VTC_CTL_VSP (1 << 2) /* vert sync output polarity */
57 #define VTC_CTL_HBP (1 << 1) /* hori blank output polarity */
58 #define VTC_CTL_VBP (1 << 0) /* vert blank output polarity */
60 #define VTC_CTL_FIPSS (1 << 26) /* field id output polarity source */
61 #define VTC_CTL_ACPSS (1 << 25) /* active chroma out polarity source */
62 #define VTC_CTL_AVPSS (1 << 24) /* active video out polarity source */
63 #define VTC_CTL_HSPSS (1 << 23) /* hori sync out polarity source */
64 #define VTC_CTL_VSPSS (1 << 22) /* vert sync out polarity source */
65 #define VTC_CTL_HBPSS (1 << 21) /* hori blank out polarity source */
66 #define VTC_CTL_VBPSS (1 << 20) /* vert blank out polarity source */
68 #define VTC_CTL_VCSS (1 << 18) /* chroma source select */
69 #define VTC_CTL_VASS (1 << 17) /* vertical offset source select */
70 #define VTC_CTL_VBSS (1 << 16) /* vertical sync end source select */
71 #define VTC_CTL_VSSS (1 << 15) /* vertical sync start source select */
72 #define VTC_CTL_VFSS (1 << 14) /* vertical active size source select */
73 #define VTC_CTL_VTSS (1 << 13) /* vertical frame size source select */
75 #define VTC_CTL_HBSS (1 << 11) /* horiz sync end source select */
76 #define VTC_CTL_HSSS (1 << 10) /* horiz sync start source select */
77 #define VTC_CTL_HFSS (1 << 9) /* horiz active size source select */
78 #define VTC_CTL_HTSS (1 << 8) /* horiz frame size source select */
80 #define VTC_CTL_GE (1 << 2) /* vtc generator enable */
81 #define VTC_CTL_RU (1 << 1) /* vtc register update */
83 /* vtc generator horizontal 1 */
84 #define VTC_GH1_BPSTART_MASK 0x1fff0000 /* horiz back porch start */
85 #define VTC_GH1_BPSTART_SHIFT 16
86 #define VTC_GH1_SYNCSTART_MASK 0x00001fff
88 /* vtc generator vertical 1 (filed 0) */
89 #define VTC_GV1_BPSTART_MASK 0x1fff0000 /* vertical back porch start */
90 #define VTC_GV1_BPSTART_SHIFT 16
91 #define VTC_GV1_SYNCSTART_MASK 0x00001fff
93 /* vtc generator/detector vblank/vsync horizontal offset registers */
94 #define VTC_XVXHOX_HEND_MASK 0x1fff0000 /* horiz offset end */
95 #define VTC_XVXHOX_HEND_SHIFT 16 /* horiz offset end shift */
96 #define VTC_XVXHOX_HSTART_MASK 0x00001fff /* horiz offset start */
98 /* reset register bit definition */
99 #define VTC_RESET_RESET (1 << 31) /* Software Reset */
101 /* interrupt status/enable register bit definition */
102 #define VTC_IXR_FSYNC15 (1 << 31) /* frame sync interrupt 15 */
103 #define VTC_IXR_FSYNC14 (1 << 30) /* frame sync interrupt 14 */
104 #define VTC_IXR_FSYNC13 (1 << 29) /* frame sync interrupt 13 */
105 #define VTC_IXR_FSYNC12 (1 << 28) /* frame sync interrupt 12 */
106 #define VTC_IXR_FSYNC11 (1 << 27) /* frame sync interrupt 11 */
107 #define VTC_IXR_FSYNC10 (1 << 26) /* frame sync interrupt 10 */
108 #define VTC_IXR_FSYNC09 (1 << 25) /* frame sync interrupt 09 */
109 #define VTC_IXR_FSYNC08 (1 << 24) /* frame sync interrupt 08 */
110 #define VTC_IXR_FSYNC07 (1 << 23) /* frame sync interrupt 07 */
111 #define VTC_IXR_FSYNC06 (1 << 22) /* frame sync interrupt 06 */
112 #define VTC_IXR_FSYNC05 (1 << 21) /* frame sync interrupt 05 */
113 #define VTC_IXR_FSYNC04 (1 << 20) /* frame sync interrupt 04 */
114 #define VTC_IXR_FSYNC03 (1 << 19) /* frame sync interrupt 03 */
115 #define VTC_IXR_FSYNC02 (1 << 18) /* frame sync interrupt 02 */
116 #define VTC_IXR_FSYNC01 (1 << 17) /* frame sync interrupt 01 */
117 #define VTC_IXR_FSYNC00 (1 << 16) /* frame sync interrupt 00 */
118 #define VTC_IXR_FSYNCALL_MASK (VTC_IXR_FSYNC00 | \
135 #define VTC_IXR_G_AV (1 << 13) /* generator actv video intr */
136 #define VTC_IXR_G_VBLANK (1 << 12) /* generator vblank interrupt */
137 #define VTC_IXR_G_ALL_MASK (VTC_IXR_G_AV | \
138 VTC_IXR_G_VBLANK) /* all generator intr */
140 #define VTC_IXR_D_AV (1 << 11) /* detector active video intr */
141 #define VTC_IXR_D_VBLANK (1 << 10) /* detector vblank interrupt */
142 #define VTC_IXR_D_ALL_MASK (VTC_IXR_D_AV | \
143 VTC_IXR_D_VBLANK) /* all detector intr */
145 #define VTC_IXR_LOL (1 << 9) /* lock loss */
146 #define VTC_IXR_LO (1 << 8) /* lock */
147 #define VTC_IXR_LOCKALL_MASK (VTC_IXR_LOL | \
148 VTC_IXR_LO) /* all signal lock intr */
150 #define VTC_IXR_ACL (1 << 21) /* active chroma signal lock */
151 #define VTC_IXR_AVL (1 << 20) /* active video signal lock */
152 #define VTC_IXR_HSL (1 << 19) /* horizontal sync signal lock */
153 #define VTC_IXR_VSL (1 << 18) /* vertical sync signal lock */
154 #define VTC_IXR_HBL (1 << 17) /* horizontal blank signal lock */
155 #define VTC_IXR_VBL (1 << 16) /* vertical blank signal lock */
157 /* mask for all interrupts */
158 #define VTC_IXR_ALLINTR_MASK (VTC_IXR_FSYNCALL_MASK | \
159 VTC_IXR_G_ALL_MASK | \
160 VTC_IXR_D_ALL_MASK | \
161 VTC_IXR_LOCKALL_MASK)
163 * struct xilinx_vtc - Xilinx VTC object
167 * @vblank_fn: vblank handler func
168 * @vblank_data: vblank handler private data
173 void (*vblank_fn)(void *);
178 * struct xilinx_vtc_polarity - vtc polarity config
180 * @active_chroma: active chroma polarity
181 * @active_video: active video polarity
182 * @field_id: field ID polarity
183 * @vblank: vblank polarity
184 * @vsync: vsync polarity
185 * @hblank: hblank polarity
186 * @hsync: hsync polarity
188 struct xilinx_vtc_polarity {
199 * struct xilinx_vtc_hori_offset - vtc horizontal offset config
201 * @vblank_hori_start: vblank horizontal start
202 * @vblank_hori_end: vblank horizontal end
203 * @vsync_hori_start: vsync horizontal start
204 * @vsync_hori_end: vsync horizontal end
206 struct xilinx_vtc_hori_offset {
207 u16 vblank_hori_start;
209 u16 vsync_hori_start;
214 * struct xilinx_vtc_src_config - vtc source config
216 * @field_id_pol: filed id polarity source
217 * @active_chroma_pol: active chroma polarity source
218 * @active_video_pol: active video polarity source
219 * @hsync_pol: hsync polarity source
220 * @vsync_pol: vsync polarity source
221 * @hblank_pol: hblnak polarity source
222 * @vblank_pol: vblank polarity source
223 * @vchroma: vchroma polarity start source
224 * @vactive: vactive size source
225 * @vbackporch: vbackporch start source
226 * @vsync: vsync start source
227 * @vfrontporch: vfrontporch start source
228 * @vtotal: vtotal size source
229 * @hactive: hactive start source
230 * @hbackporch: hbackporch start source
231 * @hsync: hsync start source
232 * @hfrontporch: hfrontporch start source
233 * @htotal: htotal size source
235 struct xilinx_vtc_src_config {
237 u8 active_chroma_pol;
258 /* configure polarity of signals */
259 static void xilinx_vtc_config_polarity(struct xilinx_vtc *vtc,
260 struct xilinx_vtc_polarity *polarity)
264 reg = xilinx_drm_readl(vtc->base, VTC_GPOL);
266 if (polarity->active_chroma)
268 if (polarity->active_video)
270 if (polarity->field_id)
272 if (polarity->vblank)
276 if (polarity->hblank)
281 xilinx_drm_writel(vtc->base, VTC_GPOL, reg);
284 /* configure horizontal offset */
286 xilinx_vtc_config_hori_offset(struct xilinx_vtc *vtc,
287 struct xilinx_vtc_hori_offset *hori_offset)
291 reg = hori_offset->vblank_hori_start & VTC_XVXHOX_HSTART_MASK;
292 reg |= (hori_offset->vblank_hori_end << VTC_XVXHOX_HEND_SHIFT) &
293 VTC_XVXHOX_HEND_MASK;
294 xilinx_drm_writel(vtc->base, VTC_GVBHOFF, reg);
296 reg = hori_offset->vsync_hori_start & VTC_XVXHOX_HSTART_MASK;
297 reg |= (hori_offset->vsync_hori_end << VTC_XVXHOX_HEND_SHIFT) &
298 VTC_XVXHOX_HEND_MASK;
299 xilinx_drm_writel(vtc->base, VTC_GVSHOFF, reg);
302 /* configure source */
303 static void xilinx_vtc_config_src(struct xilinx_vtc *vtc,
304 struct xilinx_vtc_src_config *src_config)
308 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
310 if (src_config->field_id_pol)
311 reg |= VTC_CTL_FIPSS;
312 if (src_config->active_chroma_pol)
313 reg |= VTC_CTL_ACPSS;
314 if (src_config->active_video_pol)
315 reg |= VTC_CTL_AVPSS;
316 if (src_config->hsync_pol)
317 reg |= VTC_CTL_HSPSS;
318 if (src_config->vsync_pol)
319 reg |= VTC_CTL_VSPSS;
320 if (src_config->hblank_pol)
321 reg |= VTC_CTL_HBPSS;
322 if (src_config->vblank_pol)
323 reg |= VTC_CTL_VBPSS;
325 if (src_config->vchroma)
327 if (src_config->vactive)
329 if (src_config->vbackporch)
331 if (src_config->vsync)
333 if (src_config->vfrontporch)
335 if (src_config->vtotal)
338 if (src_config->hbackporch)
340 if (src_config->hsync)
342 if (src_config->hfrontporch)
344 if (src_config->htotal)
347 xilinx_drm_writel(vtc->base, VTC_CTL, reg);
351 void xilinx_vtc_enable(struct xilinx_vtc *vtc)
355 /* enable a generator only for now */
356 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
357 xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_GE);
361 void xilinx_vtc_disable(struct xilinx_vtc *vtc)
365 /* disable a generator only for now */
366 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
367 xilinx_drm_writel(vtc->base, VTC_CTL, reg & ~VTC_CTL_GE);
370 /* configure vtc signals */
371 void xilinx_vtc_config_sig(struct xilinx_vtc *vtc,
372 struct videomode *vm)
375 u32 htotal, hactive, hsync_start, hbackporch_start;
376 u32 vtotal, vactive, vsync_start, vbackporch_start;
377 struct xilinx_vtc_hori_offset hori_offset;
378 struct xilinx_vtc_polarity polarity;
379 struct xilinx_vtc_src_config src;
381 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
382 xilinx_drm_writel(vtc->base, VTC_CTL, reg & ~VTC_CTL_RU);
384 htotal = vm->hactive + vm->hfront_porch + vm->hsync_len +
386 vtotal = vm->vactive + vm->vfront_porch + vm->vsync_len +
389 hactive = vm->hactive;
390 vactive = vm->vactive;
392 hsync_start = vm->hactive + vm->hfront_porch;
393 vsync_start = vm->vactive + vm->vfront_porch;
395 hbackporch_start = hsync_start + vm->hsync_len;
396 vbackporch_start = vsync_start + vm->vsync_len;
398 reg = htotal & 0x1fff;
399 xilinx_drm_writel(vtc->base, VTC_GHSIZE, reg);
401 reg = vtotal & 0x1fff;
402 xilinx_drm_writel(vtc->base, VTC_GVSIZE, reg);
404 DRM_DEBUG_DRIVER("ht: %d, vt: %d\n", htotal, vtotal);
406 reg = hactive & 0x1fff;
407 reg |= (vactive & 0x1fff) << 16;
408 xilinx_drm_writel(vtc->base, VTC_GASIZE, reg);
410 DRM_DEBUG_DRIVER("ha: %d, va: %d\n", hactive, vactive);
412 reg = hsync_start & VTC_GH1_SYNCSTART_MASK;
413 reg |= (hbackporch_start << VTC_GH1_BPSTART_SHIFT) &
414 VTC_GH1_BPSTART_MASK;
415 xilinx_drm_writel(vtc->base, VTC_GHSYNC, reg);
417 DRM_DEBUG_DRIVER("hs: %d, hb: %d\n", hsync_start, hbackporch_start);
419 reg = vsync_start & VTC_GV1_SYNCSTART_MASK;
420 reg |= (vbackporch_start << VTC_GV1_BPSTART_SHIFT) &
421 VTC_GV1_BPSTART_MASK;
422 xilinx_drm_writel(vtc->base, VTC_GVSYNC, reg);
424 DRM_DEBUG_DRIVER("vs: %d, vb: %d\n", vsync_start, vbackporch_start);
426 hori_offset.vblank_hori_start = hactive;
427 hori_offset.vblank_hori_end = hactive;
428 hori_offset.vsync_hori_start = hactive;
429 hori_offset.vsync_hori_end = hactive;
431 xilinx_vtc_config_hori_offset(vtc, &hori_offset);
433 /* set up polarity */
434 memset(&polarity, 0x0, sizeof(polarity));
439 polarity.active_video = 1;
440 polarity.active_chroma = 1;
441 polarity.field_id = 1;
442 xilinx_vtc_config_polarity(vtc, &polarity);
444 /* set up src config */
445 memset(&src, 0x0, sizeof(src));
457 xilinx_vtc_config_src(vtc, &src);
459 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
460 xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_RU);
464 void xilinx_vtc_reset(struct xilinx_vtc *vtc)
468 xilinx_drm_writel(vtc->base, VTC_RESET, VTC_RESET_RESET);
470 /* enable register update */
471 reg = xilinx_drm_readl(vtc->base, VTC_CTL);
472 xilinx_drm_writel(vtc->base, VTC_CTL, reg | VTC_CTL_RU);
475 /* enable interrupt */
476 static inline void xilinx_vtc_intr_enable(struct xilinx_vtc *vtc, u32 intr)
478 xilinx_drm_writel(vtc->base, VTC_IER, (intr & VTC_IXR_ALLINTR_MASK) |
479 xilinx_drm_readl(vtc->base, VTC_IER));
482 /* disable interrupt */
483 static inline void xilinx_vtc_intr_disable(struct xilinx_vtc *vtc, u32 intr)
485 xilinx_drm_writel(vtc->base, VTC_IER, ~(intr & VTC_IXR_ALLINTR_MASK) &
486 xilinx_drm_readl(vtc->base, VTC_IER));
490 static inline u32 xilinx_vtc_intr_get(struct xilinx_vtc *vtc)
492 return xilinx_drm_readl(vtc->base, VTC_IER) &
493 xilinx_drm_readl(vtc->base, VTC_ISR) & VTC_IXR_ALLINTR_MASK;
496 /* clear interrupt */
497 static inline void xilinx_vtc_intr_clear(struct xilinx_vtc *vtc, u32 intr)
499 xilinx_drm_writel(vtc->base, VTC_ISR, intr & VTC_IXR_ALLINTR_MASK);
502 /* interrupt handler */
503 static irqreturn_t xilinx_vtc_intr_handler(int irq, void *data)
505 struct xilinx_vtc *vtc = data;
507 u32 intr = xilinx_vtc_intr_get(vtc);
512 if ((intr & VTC_IXR_G_VBLANK) && (vtc->vblank_fn))
513 vtc->vblank_fn(vtc->vblank_data);
515 xilinx_vtc_intr_clear(vtc, intr);
520 /* enable vblank interrupt */
521 void xilinx_vtc_enable_vblank_intr(struct xilinx_vtc *vtc,
522 void (*vblank_fn)(void *),
525 vtc->vblank_fn = vblank_fn;
526 vtc->vblank_data = vblank_priv;
527 xilinx_vtc_intr_enable(vtc, VTC_IXR_G_VBLANK);
530 /* disable vblank interrupt */
531 void xilinx_vtc_disable_vblank_intr(struct xilinx_vtc *vtc)
533 xilinx_vtc_intr_disable(vtc, VTC_IXR_G_VBLANK);
534 vtc->vblank_data = NULL;
535 vtc->vblank_fn = NULL;
538 static const struct of_device_id xilinx_vtc_of_match[] = {
539 { .compatible = "xlnx,v-tc-5.01.a" },
540 { /* end of table */ },
544 struct xilinx_vtc *xilinx_vtc_probe(struct device *dev,
545 struct device_node *node)
547 struct xilinx_vtc *vtc;
548 const struct of_device_id *match;
552 match = of_match_node(xilinx_vtc_of_match, node);
554 dev_err(dev, "failed to match the device node\n");
555 return ERR_PTR(-ENODEV);
558 vtc = devm_kzalloc(dev, sizeof(*vtc), GFP_KERNEL);
560 return ERR_PTR(-ENOMEM);
562 ret = of_address_to_resource(node, 0, &res);
564 dev_err(dev, "failed to of_address_to_resource\n");
568 vtc->base = devm_ioremap_resource(dev, &res);
569 if (IS_ERR(vtc->base))
570 return ERR_CAST(vtc->base);
572 xilinx_vtc_intr_disable(vtc, VTC_IXR_ALLINTR_MASK);
573 vtc->irq = irq_of_parse_and_map(node, 0);
575 ret = devm_request_irq(dev, vtc->irq, xilinx_vtc_intr_handler,
576 IRQF_SHARED, "xilinx_vtc", vtc);
578 dev_warn(dev, "failed to requet_irq() for vtc\n");
583 xilinx_vtc_reset(vtc);