2 * Copyright (C) 2015 Broadcom
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
10 * DOC: VC4 HVS module.
12 * The HVS is the piece of hardware that does translation, scaling,
13 * colorspace conversion, and compositing of pixels stored in
14 * framebuffers into a FIFO of pixels going out to the Pixel Valve
15 * (CRTC). It operates at the system clock rate (the system audio
16 * clock gate, specifically), which is much higher than the pixel
19 * There is a single global HVS, with multiple output FIFOs that can
20 * be consumed by the PVs. This file just manages the resources for
21 * the HVS, while the vc4_crtc.c code actually drives HVS setup for
25 #include "linux/component.h"
29 #define HVS_REG(reg) { reg, #reg }
34 HVS_REG(SCALER_DISPCTRL
),
35 HVS_REG(SCALER_DISPSTAT
),
36 HVS_REG(SCALER_DISPID
),
37 HVS_REG(SCALER_DISPECTRL
),
38 HVS_REG(SCALER_DISPPROF
),
39 HVS_REG(SCALER_DISPDITHER
),
40 HVS_REG(SCALER_DISPEOLN
),
41 HVS_REG(SCALER_DISPLIST0
),
42 HVS_REG(SCALER_DISPLIST1
),
43 HVS_REG(SCALER_DISPLIST2
),
44 HVS_REG(SCALER_DISPLSTAT
),
45 HVS_REG(SCALER_DISPLACT0
),
46 HVS_REG(SCALER_DISPLACT1
),
47 HVS_REG(SCALER_DISPLACT2
),
48 HVS_REG(SCALER_DISPCTRL0
),
49 HVS_REG(SCALER_DISPBKGND0
),
50 HVS_REG(SCALER_DISPSTAT0
),
51 HVS_REG(SCALER_DISPBASE0
),
52 HVS_REG(SCALER_DISPCTRL1
),
53 HVS_REG(SCALER_DISPBKGND1
),
54 HVS_REG(SCALER_DISPSTAT1
),
55 HVS_REG(SCALER_DISPBASE1
),
56 HVS_REG(SCALER_DISPCTRL2
),
57 HVS_REG(SCALER_DISPBKGND2
),
58 HVS_REG(SCALER_DISPSTAT2
),
59 HVS_REG(SCALER_DISPBASE2
),
60 HVS_REG(SCALER_DISPALPHA2
),
63 void vc4_hvs_dump_state(struct drm_device
*dev
)
65 struct vc4_dev
*vc4
= to_vc4_dev(dev
);
68 for (i
= 0; i
< ARRAY_SIZE(hvs_regs
); i
++) {
69 DRM_INFO("0x%04x (%s): 0x%08x\n",
70 hvs_regs
[i
].reg
, hvs_regs
[i
].name
,
71 HVS_READ(hvs_regs
[i
].reg
));
74 DRM_INFO("HVS ctx:\n");
75 for (i
= 0; i
< 64; i
+= 4) {
76 DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
77 i
* 4, i
< HVS_BOOTLOADER_DLIST_END
? "B" : "D",
78 readl((u32 __iomem
*)vc4
->hvs
->dlist
+ i
+ 0),
79 readl((u32 __iomem
*)vc4
->hvs
->dlist
+ i
+ 1),
80 readl((u32 __iomem
*)vc4
->hvs
->dlist
+ i
+ 2),
81 readl((u32 __iomem
*)vc4
->hvs
->dlist
+ i
+ 3));
85 #ifdef CONFIG_DEBUG_FS
86 int vc4_hvs_debugfs_regs(struct seq_file
*m
, void *unused
)
88 struct drm_info_node
*node
= (struct drm_info_node
*)m
->private;
89 struct drm_device
*dev
= node
->minor
->dev
;
90 struct vc4_dev
*vc4
= to_vc4_dev(dev
);
93 for (i
= 0; i
< ARRAY_SIZE(hvs_regs
); i
++) {
94 seq_printf(m
, "%s (0x%04x): 0x%08x\n",
95 hvs_regs
[i
].name
, hvs_regs
[i
].reg
,
96 HVS_READ(hvs_regs
[i
].reg
));
103 /* The filter kernel is composed of dwords each containing 3 9-bit
104 * signed integers packed next to each other.
106 #define VC4_INT_TO_COEFF(coeff) (coeff & 0x1ff)
107 #define VC4_PPF_FILTER_WORD(c0, c1, c2) \
108 ((((c0) & 0x1ff) << 0) | \
109 (((c1) & 0x1ff) << 9) | \
110 (((c2) & 0x1ff) << 18))
112 /* The whole filter kernel is arranged as the coefficients 0-16 going
113 * up, then a pad, then 17-31 going down and reversed within the
114 * dwords. This means that a linear phase kernel (where it's
115 * symmetrical at the boundary between 15 and 16) has the last 5
116 * dwords matching the first 5, but reversed.
118 #define VC4_LINEAR_PHASE_KERNEL(c0, c1, c2, c3, c4, c5, c6, c7, c8, \
119 c9, c10, c11, c12, c13, c14, c15) \
120 {VC4_PPF_FILTER_WORD(c0, c1, c2), \
121 VC4_PPF_FILTER_WORD(c3, c4, c5), \
122 VC4_PPF_FILTER_WORD(c6, c7, c8), \
123 VC4_PPF_FILTER_WORD(c9, c10, c11), \
124 VC4_PPF_FILTER_WORD(c12, c13, c14), \
125 VC4_PPF_FILTER_WORD(c15, c15, 0)}
127 #define VC4_LINEAR_PHASE_KERNEL_DWORDS 6
128 #define VC4_KERNEL_DWORDS (VC4_LINEAR_PHASE_KERNEL_DWORDS * 2 - 1)
130 /* Recommended B=1/3, C=1/3 filter choice from Mitchell/Netravali.
131 * http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf
133 static const u32 mitchell_netravali_1_3_1_3_kernel
[] =
134 VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18,
135 50, 82, 119, 155, 187, 213, 227);
137 static int vc4_hvs_upload_linear_kernel(struct vc4_hvs
*hvs
,
138 struct drm_mm_node
*space
,
142 u32 __iomem
*dst_kernel
;
144 ret
= drm_mm_insert_node(&hvs
->dlist_mm
, space
, VC4_KERNEL_DWORDS
, 1,
147 DRM_ERROR("Failed to allocate space for filter kernel: %d\n",
152 dst_kernel
= hvs
->dlist
+ space
->start
;
154 for (i
= 0; i
< VC4_KERNEL_DWORDS
; i
++) {
155 if (i
< VC4_LINEAR_PHASE_KERNEL_DWORDS
)
156 writel(kernel
[i
], &dst_kernel
[i
]);
158 writel(kernel
[VC4_KERNEL_DWORDS
- i
- 1],
166 static int vc4_hvs_bind(struct device
*dev
, struct device
*master
, void *data
)
168 struct platform_device
*pdev
= to_platform_device(dev
);
169 struct drm_device
*drm
= dev_get_drvdata(master
);
170 struct vc4_dev
*vc4
= drm
->dev_private
;
171 struct vc4_hvs
*hvs
= NULL
;
174 hvs
= devm_kzalloc(&pdev
->dev
, sizeof(*hvs
), GFP_KERNEL
);
180 hvs
->regs
= vc4_ioremap_regs(pdev
, 0);
181 if (IS_ERR(hvs
->regs
))
182 return PTR_ERR(hvs
->regs
);
184 hvs
->dlist
= hvs
->regs
+ SCALER_DLIST_START
;
186 spin_lock_init(&hvs
->mm_lock
);
188 /* Set up the HVS display list memory manager. We never
189 * overwrite the setup from the bootloader (just 128b out of
190 * our 16K), since we don't want to scramble the screen when
191 * transitioning from the firmware's boot setup to runtime.
193 drm_mm_init(&hvs
->dlist_mm
,
194 HVS_BOOTLOADER_DLIST_END
,
195 (SCALER_DLIST_SIZE
>> 2) - HVS_BOOTLOADER_DLIST_END
);
197 /* Set up the HVS LBM memory manager. We could have some more
198 * complicated data structure that allowed reuse of LBM areas
199 * between planes when they don't overlap on the screen, but
200 * for now we just allocate globally.
202 drm_mm_init(&hvs
->lbm_mm
, 0, 96 * 1024);
204 /* Upload filter kernels. We only have the one for now, so we
205 * keep it around for the lifetime of the driver.
207 ret
= vc4_hvs_upload_linear_kernel(hvs
,
208 &hvs
->mitchell_netravali_filter
,
209 mitchell_netravali_1_3_1_3_kernel
);
217 static void vc4_hvs_unbind(struct device
*dev
, struct device
*master
,
220 struct drm_device
*drm
= dev_get_drvdata(master
);
221 struct vc4_dev
*vc4
= drm
->dev_private
;
223 if (vc4
->hvs
->mitchell_netravali_filter
.allocated
)
224 drm_mm_remove_node(&vc4
->hvs
->mitchell_netravali_filter
);
226 drm_mm_takedown(&vc4
->hvs
->dlist_mm
);
227 drm_mm_takedown(&vc4
->hvs
->lbm_mm
);
232 static const struct component_ops vc4_hvs_ops
= {
233 .bind
= vc4_hvs_bind
,
234 .unbind
= vc4_hvs_unbind
,
237 static int vc4_hvs_dev_probe(struct platform_device
*pdev
)
239 return component_add(&pdev
->dev
, &vc4_hvs_ops
);
242 static int vc4_hvs_dev_remove(struct platform_device
*pdev
)
244 component_del(&pdev
->dev
, &vc4_hvs_ops
);
248 static const struct of_device_id vc4_hvs_dt_match
[] = {
249 { .compatible
= "brcm,bcm2835-hvs" },
253 struct platform_driver vc4_hvs_driver
= {
254 .probe
= vc4_hvs_dev_probe
,
255 .remove
= vc4_hvs_dev_remove
,
258 .of_match_table
= vc4_hvs_dt_match
,