diff options
author | Eric Anholt <eric@anholt.net> | 2015-10-20 11:06:57 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2016-02-16 14:24:08 -0500 |
commit | 21af94cf1a4c2d3450ab7fead58e6e2291ab92a9 (patch) | |
tree | 48339e5624f09e8bb6f499ecdad676c119f56dcb | |
parent | f863e356013d628fa65b1cd89aa298eed26fc936 (diff) |
drm/vc4: Add support for scaling of display planes.
This implements a simple policy for choosing scaling modes
(trapezoidal for decimation, PPF for magnification), and a single PPF
filter (Mitchell/Netravali's recommendation).
Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hvs.c | 84 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_plane.c | 253 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 46 |
4 files changed, 374 insertions, 13 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index ae9802486080..3d1df6b1c4d3 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h | |||
@@ -155,7 +155,11 @@ struct vc4_hvs { | |||
155 | * list. Units are dwords. | 155 | * list. Units are dwords. |
156 | */ | 156 | */ |
157 | struct drm_mm dlist_mm; | 157 | struct drm_mm dlist_mm; |
158 | /* Memory manager for the LBM memory used by HVS scaling. */ | ||
159 | struct drm_mm lbm_mm; | ||
158 | spinlock_t mm_lock; | 160 | spinlock_t mm_lock; |
161 | |||
162 | struct drm_mm_node mitchell_netravali_filter; | ||
159 | }; | 163 | }; |
160 | 164 | ||
161 | struct vc4_plane { | 165 | struct vc4_plane { |
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 9e435545b3b6..6fbab1c82cb1 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c | |||
@@ -100,12 +100,76 @@ int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused) | |||
100 | } | 100 | } |
101 | #endif | 101 | #endif |
102 | 102 | ||
103 | /* The filter kernel is composed of dwords each containing 3 9-bit | ||
104 | * signed integers packed next to each other. | ||
105 | */ | ||
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)) | ||
111 | |||
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. | ||
117 | */ | ||
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)} | ||
126 | |||
127 | #define VC4_LINEAR_PHASE_KERNEL_DWORDS 6 | ||
128 | #define VC4_KERNEL_DWORDS (VC4_LINEAR_PHASE_KERNEL_DWORDS * 2 - 1) | ||
129 | |||
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 | ||
132 | */ | ||
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); | ||
136 | |||
137 | static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, | ||
138 | struct drm_mm_node *space, | ||
139 | const u32 *kernel) | ||
140 | { | ||
141 | int ret, i; | ||
142 | u32 __iomem *dst_kernel; | ||
143 | |||
144 | ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS, 1, | ||
145 | 0); | ||
146 | if (ret) { | ||
147 | DRM_ERROR("Failed to allocate space for filter kernel: %d\n", | ||
148 | ret); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | dst_kernel = hvs->dlist + space->start; | ||
153 | |||
154 | for (i = 0; i < VC4_KERNEL_DWORDS; i++) { | ||
155 | if (i < VC4_LINEAR_PHASE_KERNEL_DWORDS) | ||
156 | writel(kernel[i], &dst_kernel[i]); | ||
157 | else { | ||
158 | writel(kernel[VC4_KERNEL_DWORDS - i - 1], | ||
159 | &dst_kernel[i]); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
103 | static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) | 166 | static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) |
104 | { | 167 | { |
105 | struct platform_device *pdev = to_platform_device(dev); | 168 | struct platform_device *pdev = to_platform_device(dev); |
106 | struct drm_device *drm = dev_get_drvdata(master); | 169 | struct drm_device *drm = dev_get_drvdata(master); |
107 | struct vc4_dev *vc4 = drm->dev_private; | 170 | struct vc4_dev *vc4 = drm->dev_private; |
108 | struct vc4_hvs *hvs = NULL; | 171 | struct vc4_hvs *hvs = NULL; |
172 | int ret; | ||
109 | 173 | ||
110 | hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); | 174 | hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); |
111 | if (!hvs) | 175 | if (!hvs) |
@@ -130,6 +194,22 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) | |||
130 | HVS_BOOTLOADER_DLIST_END, | 194 | HVS_BOOTLOADER_DLIST_END, |
131 | (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); | 195 | (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); |
132 | 196 | ||
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. | ||
201 | */ | ||
202 | drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024); | ||
203 | |||
204 | /* Upload filter kernels. We only have the one for now, so we | ||
205 | * keep it around for the lifetime of the driver. | ||
206 | */ | ||
207 | ret = vc4_hvs_upload_linear_kernel(hvs, | ||
208 | &hvs->mitchell_netravali_filter, | ||
209 | mitchell_netravali_1_3_1_3_kernel); | ||
210 | if (ret) | ||
211 | return ret; | ||
212 | |||
133 | vc4->hvs = hvs; | 213 | vc4->hvs = hvs; |
134 | return 0; | 214 | return 0; |
135 | } | 215 | } |
@@ -140,7 +220,11 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, | |||
140 | struct drm_device *drm = dev_get_drvdata(master); | 220 | struct drm_device *drm = dev_get_drvdata(master); |
141 | struct vc4_dev *vc4 = drm->dev_private; | 221 | struct vc4_dev *vc4 = drm->dev_private; |
142 | 222 | ||
223 | if (vc4->hvs->mitchell_netravali_filter.allocated) | ||
224 | drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); | ||
225 | |||
143 | drm_mm_takedown(&vc4->hvs->dlist_mm); | 226 | drm_mm_takedown(&vc4->hvs->dlist_mm); |
227 | drm_mm_takedown(&vc4->hvs->lbm_mm); | ||
144 | 228 | ||
145 | vc4->hvs = NULL; | 229 | vc4->hvs = NULL; |
146 | } | 230 | } |
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index d9c929096164..7c2d697e8715 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c | |||
@@ -24,6 +24,12 @@ | |||
24 | #include "drm_fb_cma_helper.h" | 24 | #include "drm_fb_cma_helper.h" |
25 | #include "drm_plane_helper.h" | 25 | #include "drm_plane_helper.h" |
26 | 26 | ||
27 | enum vc4_scaling_mode { | ||
28 | VC4_SCALING_NONE, | ||
29 | VC4_SCALING_TPZ, | ||
30 | VC4_SCALING_PPF, | ||
31 | }; | ||
32 | |||
27 | struct vc4_plane_state { | 33 | struct vc4_plane_state { |
28 | struct drm_plane_state base; | 34 | struct drm_plane_state base; |
29 | /* System memory copy of the display list for this element, computed | 35 | /* System memory copy of the display list for this element, computed |
@@ -47,13 +53,19 @@ struct vc4_plane_state { | |||
47 | 53 | ||
48 | /* Clipped coordinates of the plane on the display. */ | 54 | /* Clipped coordinates of the plane on the display. */ |
49 | int crtc_x, crtc_y, crtc_w, crtc_h; | 55 | int crtc_x, crtc_y, crtc_w, crtc_h; |
50 | /* Clipped size of the area scanned from in the FB. */ | 56 | /* Clipped area being scanned from in the FB. */ |
51 | u32 src_w, src_h; | 57 | u32 src_x, src_y, src_w, src_h; |
58 | |||
59 | enum vc4_scaling_mode x_scaling, y_scaling; | ||
60 | bool is_unity; | ||
52 | 61 | ||
53 | /* Offset to start scanning out from the start of the plane's | 62 | /* Offset to start scanning out from the start of the plane's |
54 | * BO. | 63 | * BO. |
55 | */ | 64 | */ |
56 | u32 offset; | 65 | u32 offset; |
66 | |||
67 | /* Our allocation in LBM for temporary storage during scaling. */ | ||
68 | struct drm_mm_node lbm; | ||
57 | }; | 69 | }; |
58 | 70 | ||
59 | static inline struct vc4_plane_state * | 71 | static inline struct vc4_plane_state * |
@@ -90,6 +102,16 @@ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) | |||
90 | return NULL; | 102 | return NULL; |
91 | } | 103 | } |
92 | 104 | ||
105 | static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) | ||
106 | { | ||
107 | if (dst > src) | ||
108 | return VC4_SCALING_PPF; | ||
109 | else if (dst < src) | ||
110 | return VC4_SCALING_TPZ; | ||
111 | else | ||
112 | return VC4_SCALING_NONE; | ||
113 | } | ||
114 | |||
93 | static bool plane_enabled(struct drm_plane_state *state) | 115 | static bool plane_enabled(struct drm_plane_state *state) |
94 | { | 116 | { |
95 | return state->fb && state->crtc; | 117 | return state->fb && state->crtc; |
@@ -106,6 +128,8 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane | |||
106 | if (!vc4_state) | 128 | if (!vc4_state) |
107 | return NULL; | 129 | return NULL; |
108 | 130 | ||
131 | memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm)); | ||
132 | |||
109 | __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); | 133 | __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); |
110 | 134 | ||
111 | if (vc4_state->dlist) { | 135 | if (vc4_state->dlist) { |
@@ -125,8 +149,17 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane | |||
125 | static void vc4_plane_destroy_state(struct drm_plane *plane, | 149 | static void vc4_plane_destroy_state(struct drm_plane *plane, |
126 | struct drm_plane_state *state) | 150 | struct drm_plane_state *state) |
127 | { | 151 | { |
152 | struct vc4_dev *vc4 = to_vc4_dev(plane->dev); | ||
128 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | 153 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
129 | 154 | ||
155 | if (vc4_state->lbm.allocated) { | ||
156 | unsigned long irqflags; | ||
157 | |||
158 | spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); | ||
159 | drm_mm_remove_node(&vc4_state->lbm); | ||
160 | spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); | ||
161 | } | ||
162 | |||
130 | kfree(vc4_state->dlist); | 163 | kfree(vc4_state->dlist); |
131 | __drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base); | 164 | __drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base); |
132 | kfree(state); | 165 | kfree(state); |
@@ -165,23 +198,60 @@ static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) | |||
165 | vc4_state->dlist[vc4_state->dlist_count++] = val; | 198 | vc4_state->dlist[vc4_state->dlist_count++] = val; |
166 | } | 199 | } |
167 | 200 | ||
201 | /* Returns the scl0/scl1 field based on whether the dimensions need to | ||
202 | * be up/down/non-scaled. | ||
203 | * | ||
204 | * This is a replication of a table from the spec. | ||
205 | */ | ||
206 | static u32 vc4_get_scl_field(struct drm_plane_state *state) | ||
207 | { | ||
208 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | ||
209 | |||
210 | switch (vc4_state->x_scaling << 2 | vc4_state->y_scaling) { | ||
211 | case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: | ||
212 | return SCALER_CTL0_SCL_H_PPF_V_PPF; | ||
213 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: | ||
214 | return SCALER_CTL0_SCL_H_TPZ_V_PPF; | ||
215 | case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ: | ||
216 | return SCALER_CTL0_SCL_H_PPF_V_TPZ; | ||
217 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ: | ||
218 | return SCALER_CTL0_SCL_H_TPZ_V_TPZ; | ||
219 | case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE: | ||
220 | return SCALER_CTL0_SCL_H_PPF_V_NONE; | ||
221 | case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF: | ||
222 | return SCALER_CTL0_SCL_H_NONE_V_PPF; | ||
223 | case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ: | ||
224 | return SCALER_CTL0_SCL_H_NONE_V_TPZ; | ||
225 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE: | ||
226 | return SCALER_CTL0_SCL_H_TPZ_V_NONE; | ||
227 | default: | ||
228 | case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE: | ||
229 | /* The unity case is independently handled by | ||
230 | * SCALER_CTL0_UNITY. | ||
231 | */ | ||
232 | return 0; | ||
233 | } | ||
234 | } | ||
235 | |||
168 | static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | 236 | static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) |
169 | { | 237 | { |
238 | struct drm_plane *plane = state->plane; | ||
170 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | 239 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
171 | struct drm_framebuffer *fb = state->fb; | 240 | struct drm_framebuffer *fb = state->fb; |
241 | u32 subpixel_src_mask = (1 << 16) - 1; | ||
172 | 242 | ||
173 | vc4_state->offset = fb->offsets[0]; | 243 | vc4_state->offset = fb->offsets[0]; |
174 | 244 | ||
175 | if (state->crtc_w << 16 != state->src_w || | 245 | /* We don't support subpixel source positioning for scaling. */ |
176 | state->crtc_h << 16 != state->src_h) { | 246 | if ((state->src_x & subpixel_src_mask) || |
177 | /* We don't support scaling yet, which involves | 247 | (state->src_y & subpixel_src_mask) || |
178 | * allocating the LBM memory for scaling temporary | 248 | (state->src_w & subpixel_src_mask) || |
179 | * storage, and putting filter kernels in the HVS | 249 | (state->src_h & subpixel_src_mask)) { |
180 | * context. | ||
181 | */ | ||
182 | return -EINVAL; | 250 | return -EINVAL; |
183 | } | 251 | } |
184 | 252 | ||
253 | vc4_state->src_x = state->src_x >> 16; | ||
254 | vc4_state->src_y = state->src_y >> 16; | ||
185 | vc4_state->src_w = state->src_w >> 16; | 255 | vc4_state->src_w = state->src_w >> 16; |
186 | vc4_state->src_h = state->src_h >> 16; | 256 | vc4_state->src_h = state->src_h >> 16; |
187 | 257 | ||
@@ -190,6 +260,23 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
190 | vc4_state->crtc_w = state->crtc_w; | 260 | vc4_state->crtc_w = state->crtc_w; |
191 | vc4_state->crtc_h = state->crtc_h; | 261 | vc4_state->crtc_h = state->crtc_h; |
192 | 262 | ||
263 | vc4_state->x_scaling = vc4_get_scaling_mode(vc4_state->src_w, | ||
264 | vc4_state->crtc_w); | ||
265 | vc4_state->y_scaling = vc4_get_scaling_mode(vc4_state->src_h, | ||
266 | vc4_state->crtc_h); | ||
267 | vc4_state->is_unity = (vc4_state->x_scaling == VC4_SCALING_NONE && | ||
268 | vc4_state->y_scaling == VC4_SCALING_NONE); | ||
269 | |||
270 | /* No configuring scaling on the cursor plane, since it gets | ||
271 | non-vblank-synced updates, and scaling requires requires | ||
272 | LBM changes which have to be vblank-synced. | ||
273 | */ | ||
274 | if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity) | ||
275 | return -EINVAL; | ||
276 | |||
277 | /* Clamp the on-screen start x/y to 0. The hardware doesn't | ||
278 | * support negative y, and negative x wastes bandwidth. | ||
279 | */ | ||
193 | if (vc4_state->crtc_x < 0) { | 280 | if (vc4_state->crtc_x < 0) { |
194 | vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, | 281 | vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, |
195 | 0) * | 282 | 0) * |
@@ -207,6 +294,87 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
207 | return 0; | 294 | return 0; |
208 | } | 295 | } |
209 | 296 | ||
297 | static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) | ||
298 | { | ||
299 | u32 scale, recip; | ||
300 | |||
301 | scale = (1 << 16) * src / dst; | ||
302 | |||
303 | /* The specs note that while the reciprocal would be defined | ||
304 | * as (1<<32)/scale, ~0 is close enough. | ||
305 | */ | ||
306 | recip = ~0 / scale; | ||
307 | |||
308 | vc4_dlist_write(vc4_state, | ||
309 | VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) | | ||
310 | VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE)); | ||
311 | vc4_dlist_write(vc4_state, | ||
312 | VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); | ||
313 | } | ||
314 | |||
315 | static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst) | ||
316 | { | ||
317 | u32 scale = (1 << 16) * src / dst; | ||
318 | |||
319 | vc4_dlist_write(vc4_state, | ||
320 | SCALER_PPF_AGC | | ||
321 | VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | | ||
322 | VC4_SET_FIELD(0, SCALER_PPF_IPHASE)); | ||
323 | } | ||
324 | |||
325 | static u32 vc4_lbm_size(struct drm_plane_state *state) | ||
326 | { | ||
327 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | ||
328 | /* This is the worst case number. One of the two sizes will | ||
329 | * be used depending on the scaling configuration. | ||
330 | */ | ||
331 | u32 pix_per_line = max(vc4_state->src_w, (u32)vc4_state->crtc_w); | ||
332 | u32 lbm; | ||
333 | |||
334 | if (vc4_state->is_unity) | ||
335 | return 0; | ||
336 | else if (vc4_state->y_scaling == VC4_SCALING_TPZ) | ||
337 | lbm = pix_per_line * 8; | ||
338 | else { | ||
339 | /* In special cases, this multiplier might be 12. */ | ||
340 | lbm = pix_per_line * 16; | ||
341 | } | ||
342 | |||
343 | lbm = roundup(lbm, 32); | ||
344 | |||
345 | return lbm; | ||
346 | } | ||
347 | |||
348 | static void vc4_write_scaling_parameters(struct drm_plane_state *state) | ||
349 | { | ||
350 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | ||
351 | |||
352 | /* Ch0 H-PPF Word 0: Scaling Parameters */ | ||
353 | if (vc4_state->x_scaling == VC4_SCALING_PPF) { | ||
354 | vc4_write_ppf(vc4_state, | ||
355 | vc4_state->src_w, vc4_state->crtc_w); | ||
356 | } | ||
357 | |||
358 | /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ | ||
359 | if (vc4_state->y_scaling == VC4_SCALING_PPF) { | ||
360 | vc4_write_ppf(vc4_state, | ||
361 | vc4_state->src_h, vc4_state->crtc_h); | ||
362 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||
363 | } | ||
364 | |||
365 | /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ | ||
366 | if (vc4_state->x_scaling == VC4_SCALING_TPZ) { | ||
367 | vc4_write_tpz(vc4_state, | ||
368 | vc4_state->src_w, vc4_state->crtc_w); | ||
369 | } | ||
370 | |||
371 | /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ | ||
372 | if (vc4_state->y_scaling == VC4_SCALING_TPZ) { | ||
373 | vc4_write_tpz(vc4_state, | ||
374 | vc4_state->src_h, vc4_state->crtc_h); | ||
375 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | ||
376 | } | ||
377 | } | ||
210 | 378 | ||
211 | /* Writes out a full display list for an active plane to the plane's | 379 | /* Writes out a full display list for an active plane to the plane's |
212 | * private dlist state. | 380 | * private dlist state. |
@@ -214,22 +382,50 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) | |||
214 | static int vc4_plane_mode_set(struct drm_plane *plane, | 382 | static int vc4_plane_mode_set(struct drm_plane *plane, |
215 | struct drm_plane_state *state) | 383 | struct drm_plane_state *state) |
216 | { | 384 | { |
385 | struct vc4_dev *vc4 = to_vc4_dev(plane->dev); | ||
217 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | 386 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
218 | struct drm_framebuffer *fb = state->fb; | 387 | struct drm_framebuffer *fb = state->fb; |
219 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); | 388 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); |
220 | u32 ctl0_offset = vc4_state->dlist_count; | 389 | u32 ctl0_offset = vc4_state->dlist_count; |
221 | const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); | 390 | const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); |
391 | u32 scl; | ||
392 | u32 lbm_size; | ||
393 | unsigned long irqflags; | ||
222 | int ret; | 394 | int ret; |
223 | 395 | ||
224 | ret = vc4_plane_setup_clipping_and_scaling(state); | 396 | ret = vc4_plane_setup_clipping_and_scaling(state); |
225 | if (ret) | 397 | if (ret) |
226 | return ret; | 398 | return ret; |
227 | 399 | ||
400 | /* Allocate the LBM memory that the HVS will use for temporary | ||
401 | * storage due to our scaling/format conversion. | ||
402 | */ | ||
403 | lbm_size = vc4_lbm_size(state); | ||
404 | if (lbm_size) { | ||
405 | if (!vc4_state->lbm.allocated) { | ||
406 | spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); | ||
407 | ret = drm_mm_insert_node(&vc4->hvs->lbm_mm, | ||
408 | &vc4_state->lbm, | ||
409 | lbm_size, 32, 0); | ||
410 | spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); | ||
411 | } else { | ||
412 | WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | if (ret) | ||
417 | return ret; | ||
418 | |||
419 | scl = vc4_get_scl_field(state); | ||
420 | |||
421 | /* Control word */ | ||
228 | vc4_dlist_write(vc4_state, | 422 | vc4_dlist_write(vc4_state, |
229 | SCALER_CTL0_VALID | | 423 | SCALER_CTL0_VALID | |
230 | (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | | 424 | (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | |
231 | (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | | 425 | (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | |
232 | SCALER_CTL0_UNITY); | 426 | (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | |
427 | VC4_SET_FIELD(scl, SCALER_CTL0_SCL0) | | ||
428 | VC4_SET_FIELD(scl, SCALER_CTL0_SCL1)); | ||
233 | 429 | ||
234 | /* Position Word 0: Image Positions and Alpha Value */ | 430 | /* Position Word 0: Image Positions and Alpha Value */ |
235 | vc4_state->pos0_offset = vc4_state->dlist_count; | 431 | vc4_state->pos0_offset = vc4_state->dlist_count; |
@@ -238,9 +434,14 @@ static int vc4_plane_mode_set(struct drm_plane *plane, | |||
238 | VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | | 434 | VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | |
239 | VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); | 435 | VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); |
240 | 436 | ||
241 | /* Position Word 1: Scaled Image Dimensions. | 437 | /* Position Word 1: Scaled Image Dimensions. */ |
242 | * Skipped due to SCALER_CTL0_UNITY scaling. | 438 | if (!vc4_state->is_unity) { |
243 | */ | 439 | vc4_dlist_write(vc4_state, |
440 | VC4_SET_FIELD(vc4_state->crtc_w, | ||
441 | SCALER_POS1_SCL_WIDTH) | | ||
442 | VC4_SET_FIELD(vc4_state->crtc_h, | ||
443 | SCALER_POS1_SCL_HEIGHT)); | ||
444 | } | ||
244 | 445 | ||
245 | /* Position Word 2: Source Image Size, Alpha Mode */ | 446 | /* Position Word 2: Source Image Size, Alpha Mode */ |
246 | vc4_state->pos2_offset = vc4_state->dlist_count; | 447 | vc4_state->pos2_offset = vc4_state->dlist_count; |
@@ -266,6 +467,32 @@ static int vc4_plane_mode_set(struct drm_plane *plane, | |||
266 | vc4_dlist_write(vc4_state, | 467 | vc4_dlist_write(vc4_state, |
267 | VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH)); | 468 | VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH)); |
268 | 469 | ||
470 | if (!vc4_state->is_unity) { | ||
471 | /* LBM Base Address. */ | ||
472 | if (vc4_state->y_scaling != VC4_SCALING_NONE) | ||
473 | vc4_dlist_write(vc4_state, vc4_state->lbm.start); | ||
474 | |||
475 | vc4_write_scaling_parameters(state); | ||
476 | |||
477 | /* If any PPF setup was done, then all the kernel | ||
478 | * pointers get uploaded. | ||
479 | */ | ||
480 | if (vc4_state->x_scaling == VC4_SCALING_PPF || | ||
481 | vc4_state->y_scaling == VC4_SCALING_PPF) { | ||
482 | u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, | ||
483 | SCALER_PPF_KERNEL_OFFSET); | ||
484 | |||
485 | /* HPPF plane 0 */ | ||
486 | vc4_dlist_write(vc4_state, kernel); | ||
487 | /* VPPF plane 0 */ | ||
488 | vc4_dlist_write(vc4_state, kernel); | ||
489 | /* HPPF plane 1 */ | ||
490 | vc4_dlist_write(vc4_state, kernel); | ||
491 | /* VPPF plane 1 */ | ||
492 | vc4_dlist_write(vc4_state, kernel); | ||
493 | } | ||
494 | } | ||
495 | |||
269 | vc4_state->dlist[ctl0_offset] |= | 496 | vc4_state->dlist[ctl0_offset] |= |
270 | VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); | 497 | VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); |
271 | 498 | ||
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 4e52a0a88551..037c7fe67187 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h | |||
@@ -536,6 +536,21 @@ enum hvs_pixel_format { | |||
536 | #define SCALER_CTL0_ORDER_MASK VC4_MASK(14, 13) | 536 | #define SCALER_CTL0_ORDER_MASK VC4_MASK(14, 13) |
537 | #define SCALER_CTL0_ORDER_SHIFT 13 | 537 | #define SCALER_CTL0_ORDER_SHIFT 13 |
538 | 538 | ||
539 | #define SCALER_CTL0_SCL1_MASK VC4_MASK(10, 8) | ||
540 | #define SCALER_CTL0_SCL1_SHIFT 8 | ||
541 | |||
542 | #define SCALER_CTL0_SCL0_MASK VC4_MASK(7, 5) | ||
543 | #define SCALER_CTL0_SCL0_SHIFT 5 | ||
544 | |||
545 | #define SCALER_CTL0_SCL_H_PPF_V_PPF 0 | ||
546 | #define SCALER_CTL0_SCL_H_TPZ_V_PPF 1 | ||
547 | #define SCALER_CTL0_SCL_H_PPF_V_TPZ 2 | ||
548 | #define SCALER_CTL0_SCL_H_TPZ_V_TPZ 3 | ||
549 | #define SCALER_CTL0_SCL_H_PPF_V_NONE 4 | ||
550 | #define SCALER_CTL0_SCL_H_NONE_V_PPF 5 | ||
551 | #define SCALER_CTL0_SCL_H_NONE_V_TPZ 6 | ||
552 | #define SCALER_CTL0_SCL_H_TPZ_V_NONE 7 | ||
553 | |||
539 | /* Set to indicate no scaling. */ | 554 | /* Set to indicate no scaling. */ |
540 | #define SCALER_CTL0_UNITY BIT(4) | 555 | #define SCALER_CTL0_UNITY BIT(4) |
541 | 556 | ||
@@ -551,6 +566,12 @@ enum hvs_pixel_format { | |||
551 | #define SCALER_POS0_START_X_MASK VC4_MASK(11, 0) | 566 | #define SCALER_POS0_START_X_MASK VC4_MASK(11, 0) |
552 | #define SCALER_POS0_START_X_SHIFT 0 | 567 | #define SCALER_POS0_START_X_SHIFT 0 |
553 | 568 | ||
569 | #define SCALER_POS1_SCL_HEIGHT_MASK VC4_MASK(27, 16) | ||
570 | #define SCALER_POS1_SCL_HEIGHT_SHIFT 16 | ||
571 | |||
572 | #define SCALER_POS1_SCL_WIDTH_MASK VC4_MASK(11, 0) | ||
573 | #define SCALER_POS1_SCL_WIDTH_SHIFT 0 | ||
574 | |||
554 | #define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30) | 575 | #define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30) |
555 | #define SCALER_POS2_ALPHA_MODE_SHIFT 30 | 576 | #define SCALER_POS2_ALPHA_MODE_SHIFT 30 |
556 | #define SCALER_POS2_ALPHA_MODE_PIPELINE 0 | 577 | #define SCALER_POS2_ALPHA_MODE_PIPELINE 0 |
@@ -564,6 +585,31 @@ enum hvs_pixel_format { | |||
564 | #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) | 585 | #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) |
565 | #define SCALER_POS2_WIDTH_SHIFT 0 | 586 | #define SCALER_POS2_WIDTH_SHIFT 0 |
566 | 587 | ||
588 | #define SCALER_TPZ0_VERT_RECALC BIT(31) | ||
589 | #define SCALER_TPZ0_SCALE_MASK VC4_MASK(28, 8) | ||
590 | #define SCALER_TPZ0_SCALE_SHIFT 8 | ||
591 | #define SCALER_TPZ0_IPHASE_MASK VC4_MASK(7, 0) | ||
592 | #define SCALER_TPZ0_IPHASE_SHIFT 0 | ||
593 | #define SCALER_TPZ1_RECIP_MASK VC4_MASK(15, 0) | ||
594 | #define SCALER_TPZ1_RECIP_SHIFT 0 | ||
595 | |||
596 | /* Skips interpolating coefficients to 64 phases, so just 8 are used. | ||
597 | * Required for nearest neighbor. | ||
598 | */ | ||
599 | #define SCALER_PPF_NOINTERP BIT(31) | ||
600 | /* Replaes the highest valued coefficient with one that makes all 4 | ||
601 | * sum to unity. | ||
602 | */ | ||
603 | #define SCALER_PPF_AGC BIT(30) | ||
604 | #define SCALER_PPF_SCALE_MASK VC4_MASK(24, 8) | ||
605 | #define SCALER_PPF_SCALE_SHIFT 8 | ||
606 | #define SCALER_PPF_IPHASE_MASK VC4_MASK(6, 0) | ||
607 | #define SCALER_PPF_IPHASE_SHIFT 0 | ||
608 | |||
609 | #define SCALER_PPF_KERNEL_OFFSET_MASK VC4_MASK(13, 0) | ||
610 | #define SCALER_PPF_KERNEL_OFFSET_SHIFT 0 | ||
611 | #define SCALER_PPF_KERNEL_UNCACHED BIT(31) | ||
612 | |||
567 | #define SCALER_SRC_PITCH_MASK VC4_MASK(15, 0) | 613 | #define SCALER_SRC_PITCH_MASK VC4_MASK(15, 0) |
568 | #define SCALER_SRC_PITCH_SHIFT 0 | 614 | #define SCALER_SRC_PITCH_SHIFT 0 |
569 | 615 | ||