Commit | Line | Data |
---|---|---|
26e0ca22 LP |
1 | /* |
2 | * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter | |
3 | * | |
8a1edc55 | 4 | * Copyright (C) 2013-2014 Renesas Electronics Corporation |
26e0ca22 LP |
5 | * |
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/device.h> | |
15 | ||
16 | #include <media/v4l2-subdev.h> | |
17 | ||
18 | #include "vsp1.h" | |
ef9621bc | 19 | #include "vsp1_dl.h" |
a0cdac56 | 20 | #include "vsp1_pipe.h" |
26e0ca22 LP |
21 | #include "vsp1_rwpf.h" |
22 | #include "vsp1_video.h" | |
23 | ||
0d268dcc LP |
24 | #define WPF_GEN2_MAX_WIDTH 2048U |
25 | #define WPF_GEN2_MAX_HEIGHT 2048U | |
26 | #define WPF_GEN3_MAX_WIDTH 8190U | |
27 | #define WPF_GEN3_MAX_HEIGHT 8190U | |
26e0ca22 LP |
28 | |
29 | /* ----------------------------------------------------------------------------- | |
30 | * Device Access | |
31 | */ | |
32 | ||
5e8dbbf3 LP |
33 | static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, |
34 | struct vsp1_dl_list *dl, u32 reg, u32 data) | |
26e0ca22 | 35 | { |
5e8dbbf3 | 36 | vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data); |
26e0ca22 LP |
37 | } |
38 | ||
894dde5c LP |
39 | /* ----------------------------------------------------------------------------- |
40 | * Controls | |
41 | */ | |
42 | ||
43 | enum wpf_flip_ctrl { | |
44 | WPF_CTRL_VFLIP = 0, | |
45 | WPF_CTRL_HFLIP = 1, | |
46 | WPF_CTRL_MAX, | |
47 | }; | |
48 | ||
49 | static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) | |
50 | { | |
51 | struct vsp1_rwpf *wpf = | |
52 | container_of(ctrl->handler, struct vsp1_rwpf, ctrls); | |
53 | unsigned int i; | |
54 | u32 flip = 0; | |
55 | ||
56 | switch (ctrl->id) { | |
57 | case V4L2_CID_HFLIP: | |
58 | case V4L2_CID_VFLIP: | |
59 | for (i = 0; i < WPF_CTRL_MAX; ++i) { | |
60 | if (wpf->flip.ctrls[i]) | |
61 | flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0; | |
62 | } | |
63 | ||
64 | spin_lock_irq(&wpf->flip.lock); | |
65 | wpf->flip.pending = flip; | |
66 | spin_unlock_irq(&wpf->flip.lock); | |
67 | break; | |
68 | ||
69 | default: | |
70 | return -EINVAL; | |
71 | } | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = { | |
77 | .s_ctrl = vsp1_wpf_s_ctrl, | |
78 | }; | |
79 | ||
80 | static int wpf_init_controls(struct vsp1_rwpf *wpf) | |
81 | { | |
82 | struct vsp1_device *vsp1 = wpf->entity.vsp1; | |
83 | unsigned int num_flip_ctrls; | |
84 | ||
85 | spin_lock_init(&wpf->flip.lock); | |
86 | ||
87 | if (wpf->entity.index != 0) { | |
88 | /* Only WPF0 supports flipping. */ | |
89 | num_flip_ctrls = 0; | |
90 | } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { | |
91 | /* When horizontal flip is supported the WPF implements two | |
92 | * controls (horizontal flip and vertical flip). | |
93 | */ | |
94 | num_flip_ctrls = 2; | |
95 | } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { | |
96 | /* When only vertical flip is supported the WPF implements a | |
97 | * single control (vertical flip). | |
98 | */ | |
99 | num_flip_ctrls = 1; | |
100 | } else { | |
101 | /* Otherwise flipping is not supported. */ | |
102 | num_flip_ctrls = 0; | |
103 | } | |
104 | ||
105 | vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); | |
106 | ||
107 | if (num_flip_ctrls >= 1) { | |
108 | wpf->flip.ctrls[WPF_CTRL_VFLIP] = | |
109 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, | |
110 | V4L2_CID_VFLIP, 0, 1, 1, 0); | |
111 | } | |
112 | ||
113 | if (num_flip_ctrls == 2) { | |
114 | wpf->flip.ctrls[WPF_CTRL_HFLIP] = | |
115 | v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, | |
116 | V4L2_CID_HFLIP, 0, 1, 1, 0); | |
117 | ||
118 | v4l2_ctrl_cluster(2, wpf->flip.ctrls); | |
119 | } | |
120 | ||
121 | if (wpf->ctrls.error) { | |
122 | dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", | |
123 | wpf->entity.index); | |
124 | return wpf->ctrls.error; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
26e0ca22 LP |
130 | /* ----------------------------------------------------------------------------- |
131 | * V4L2 Subdevice Core Operations | |
132 | */ | |
133 | ||
134 | static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) | |
135 | { | |
136 | struct vsp1_rwpf *wpf = to_rwpf(subdev); | |
26e0ca22 | 137 | struct vsp1_device *vsp1 = wpf->entity.vsp1; |
7578c204 | 138 | |
7b905f05 | 139 | if (enable) |
26e0ca22 | 140 | return 0; |
26e0ca22 | 141 | |
7b905f05 LP |
142 | /* Write to registers directly when stopping the stream as there will be |
143 | * no pipeline run to apply the display list. | |
629bb6d4 | 144 | */ |
7b905f05 LP |
145 | vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); |
146 | vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + | |
147 | VI6_WPF_SRCRPF, 0); | |
26e0ca22 | 148 | |
7b905f05 LP |
149 | return 0; |
150 | } | |
96bfa6a5 | 151 | |
7b905f05 LP |
152 | /* ----------------------------------------------------------------------------- |
153 | * V4L2 Subdevice Operations | |
154 | */ | |
26e0ca22 | 155 | |
eb9163d3 | 156 | static const struct v4l2_subdev_video_ops wpf_video_ops = { |
7b905f05 LP |
157 | .s_stream = wpf_s_stream, |
158 | }; | |
629bb6d4 | 159 | |
eb9163d3 | 160 | static const struct v4l2_subdev_ops wpf_ops = { |
7b905f05 | 161 | .video = &wpf_video_ops, |
c6c8efb6 | 162 | .pad = &vsp1_rwpf_pad_ops, |
7b905f05 | 163 | }; |
26e0ca22 | 164 | |
7b905f05 LP |
165 | /* ----------------------------------------------------------------------------- |
166 | * VSP1 Entity Operations | |
167 | */ | |
168 | ||
169 | static void vsp1_wpf_destroy(struct vsp1_entity *entity) | |
170 | { | |
171 | struct vsp1_rwpf *wpf = entity_to_rwpf(entity); | |
172 | ||
173 | vsp1_dlm_destroy(wpf->dlm); | |
174 | } | |
175 | ||
5e8dbbf3 | 176 | static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) |
7b905f05 LP |
177 | { |
178 | struct vsp1_rwpf *wpf = entity_to_rwpf(entity); | |
894dde5c LP |
179 | const struct v4l2_pix_format_mplane *format = &wpf->format; |
180 | struct vsp1_rwpf_memory mem = wpf->mem; | |
181 | unsigned int flip = wpf->flip.active; | |
182 | unsigned int offset; | |
183 | ||
184 | /* Update the memory offsets based on flipping configuration. The | |
185 | * destination addresses point to the locations where the VSP starts | |
186 | * writing to memory, which can be different corners of the image | |
187 | * depending on vertical flipping. Horizontal flipping is handled | |
188 | * through a line buffer and doesn't modify the start address. | |
189 | */ | |
190 | if (flip & BIT(WPF_CTRL_VFLIP)) { | |
191 | mem.addr[0] += (format->height - 1) | |
192 | * format->plane_fmt[0].bytesperline; | |
193 | ||
194 | if (format->num_planes > 1) { | |
195 | offset = (format->height / wpf->fmtinfo->vsub - 1) | |
196 | * format->plane_fmt[1].bytesperline; | |
197 | mem.addr[1] += offset; | |
198 | mem.addr[2] += offset; | |
199 | } | |
200 | } | |
7b905f05 | 201 | |
894dde5c LP |
202 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); |
203 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); | |
204 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); | |
7b905f05 LP |
205 | } |
206 | ||
83dd019d LP |
207 | static void wpf_configure(struct vsp1_entity *entity, |
208 | struct vsp1_pipeline *pipe, | |
fc845e52 | 209 | struct vsp1_dl_list *dl, bool full) |
7b905f05 | 210 | { |
7b905f05 LP |
211 | struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); |
212 | struct vsp1_device *vsp1 = wpf->entity.vsp1; | |
213 | const struct v4l2_mbus_framefmt *source_format; | |
214 | const struct v4l2_mbus_framefmt *sink_format; | |
215 | const struct v4l2_rect *crop; | |
216 | unsigned int i; | |
217 | u32 outfmt = 0; | |
218 | u32 srcrpf = 0; | |
26e0ca22 | 219 | |
d05a3310 | 220 | if (!full) { |
894dde5c LP |
221 | const unsigned int mask = BIT(WPF_CTRL_VFLIP) |
222 | | BIT(WPF_CTRL_HFLIP); | |
223 | ||
224 | spin_lock(&wpf->flip.lock); | |
225 | wpf->flip.active = (wpf->flip.active & ~mask) | |
226 | | (wpf->flip.pending & mask); | |
227 | spin_unlock(&wpf->flip.lock); | |
228 | ||
229 | outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; | |
230 | ||
231 | if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) | |
232 | outfmt |= VI6_WPF_OUTFMT_FLP; | |
233 | if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) | |
234 | outfmt |= VI6_WPF_OUTFMT_HFLP; | |
235 | ||
236 | vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); | |
fc845e52 | 237 | return; |
d05a3310 | 238 | } |
fc845e52 | 239 | |
7b905f05 | 240 | /* Cropping */ |
b7e5107e LP |
241 | crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); |
242 | ||
5e8dbbf3 | 243 | vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | |
e5ad37b6 LP |
244 | (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | |
245 | (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); | |
5e8dbbf3 | 246 | vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | |
e5ad37b6 LP |
247 | (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | |
248 | (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); | |
26e0ca22 LP |
249 | |
250 | /* Format */ | |
e790c3cb LP |
251 | sink_format = vsp1_entity_get_pad_format(&wpf->entity, |
252 | wpf->entity.config, | |
253 | RWPF_PAD_SINK); | |
254 | source_format = vsp1_entity_get_pad_format(&wpf->entity, | |
255 | wpf->entity.config, | |
256 | RWPF_PAD_SOURCE); | |
257 | ||
26e0ca22 | 258 | if (!pipe->lif) { |
7b905f05 | 259 | const struct v4l2_pix_format_mplane *format = &wpf->format; |
86960eec | 260 | const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; |
26e0ca22 LP |
261 | |
262 | outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; | |
263 | ||
7a52b6de LP |
264 | if (fmtinfo->alpha) |
265 | outfmt |= VI6_WPF_OUTFMT_PXA; | |
26e0ca22 LP |
266 | if (fmtinfo->swap_yc) |
267 | outfmt |= VI6_WPF_OUTFMT_SPYCS; | |
268 | if (fmtinfo->swap_uv) | |
269 | outfmt |= VI6_WPF_OUTFMT_SPUVS; | |
270 | ||
7b905f05 | 271 | /* Destination stride and byte swapping. */ |
5e8dbbf3 | 272 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y, |
7b905f05 LP |
273 | format->plane_fmt[0].bytesperline); |
274 | if (format->num_planes > 1) | |
5e8dbbf3 | 275 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C, |
7b905f05 LP |
276 | format->plane_fmt[1].bytesperline); |
277 | ||
5e8dbbf3 | 278 | vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); |
894dde5c LP |
279 | |
280 | if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && | |
281 | wpf->entity.index == 0) | |
282 | vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, | |
283 | VI6_WPF_ROT_CTRL_LN16 | | |
284 | (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); | |
26e0ca22 LP |
285 | } |
286 | ||
e790c3cb | 287 | if (sink_format->code != source_format->code) |
26e0ca22 LP |
288 | outfmt |= VI6_WPF_OUTFMT_CSC; |
289 | ||
d05a3310 | 290 | wpf->outfmt = outfmt; |
26e0ca22 | 291 | |
5e8dbbf3 LP |
292 | vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), |
293 | VI6_DPR_WPF_FPORCH_FP_WPFN); | |
26e0ca22 | 294 | |
5e8dbbf3 | 295 | vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); |
26e0ca22 | 296 | |
7b905f05 LP |
297 | /* Sources. If the pipeline has a single input and BRU is not used, |
298 | * configure it as the master layer. Otherwise configure all | |
299 | * inputs as sub-layers and select the virtual RPF as the master | |
300 | * layer. | |
301 | */ | |
302 | for (i = 0; i < vsp1->info->rpf_count; ++i) { | |
303 | struct vsp1_rwpf *input = pipe->inputs[i]; | |
26e0ca22 | 304 | |
7b905f05 LP |
305 | if (!input) |
306 | continue; | |
26e0ca22 | 307 | |
7b905f05 LP |
308 | srcrpf |= (!pipe->bru && pipe->num_inputs == 1) |
309 | ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) | |
310 | : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); | |
311 | } | |
52434534 | 312 | |
7b905f05 LP |
313 | if (pipe->bru || pipe->num_inputs > 1) |
314 | srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; | |
52434534 | 315 | |
5e8dbbf3 | 316 | vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf); |
52434534 | 317 | |
7b905f05 | 318 | /* Enable interrupts */ |
5e8dbbf3 LP |
319 | vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); |
320 | vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), | |
321 | VI6_WFP_IRQ_ENB_FREE); | |
26e0ca22 LP |
322 | } |
323 | ||
52434534 LP |
324 | static const struct vsp1_entity_operations wpf_entity_ops = { |
325 | .destroy = vsp1_wpf_destroy, | |
b58faa95 | 326 | .set_memory = wpf_set_memory, |
7b905f05 | 327 | .configure = wpf_configure, |
26e0ca22 LP |
328 | }; |
329 | ||
330 | /* ----------------------------------------------------------------------------- | |
331 | * Initialization and Cleanup | |
332 | */ | |
333 | ||
334 | struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) | |
335 | { | |
26e0ca22 | 336 | struct vsp1_rwpf *wpf; |
823329df | 337 | char name[6]; |
26e0ca22 LP |
338 | int ret; |
339 | ||
340 | wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); | |
341 | if (wpf == NULL) | |
342 | return ERR_PTR(-ENOMEM); | |
343 | ||
0d268dcc LP |
344 | if (vsp1->info->gen == 2) { |
345 | wpf->max_width = WPF_GEN2_MAX_WIDTH; | |
346 | wpf->max_height = WPF_GEN2_MAX_HEIGHT; | |
347 | } else { | |
348 | wpf->max_width = WPF_GEN3_MAX_WIDTH; | |
349 | wpf->max_height = WPF_GEN3_MAX_HEIGHT; | |
350 | } | |
26e0ca22 | 351 | |
52434534 | 352 | wpf->entity.ops = &wpf_entity_ops; |
26e0ca22 LP |
353 | wpf->entity.type = VSP1_ENTITY_WPF; |
354 | wpf->entity.index = index; | |
26e0ca22 | 355 | |
823329df | 356 | sprintf(name, "wpf.%u", index); |
6a8e07b2 LP |
357 | ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops, |
358 | MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); | |
26e0ca22 LP |
359 | if (ret < 0) |
360 | return ERR_PTR(ret); | |
361 | ||
351bbf99 LP |
362 | /* Initialize the display list manager. */ |
363 | wpf->dlm = vsp1_dlm_create(vsp1, index, 4); | |
364 | if (!wpf->dlm) { | |
365 | ret = -ENOMEM; | |
366 | goto error; | |
ef9621bc LP |
367 | } |
368 | ||
7578c204 | 369 | /* Initialize the control handler. */ |
894dde5c | 370 | ret = wpf_init_controls(wpf); |
bd2fdd5a | 371 | if (ret < 0) { |
7578c204 LP |
372 | dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", |
373 | index); | |
7578c204 LP |
374 | goto error; |
375 | } | |
376 | ||
d05a3310 LP |
377 | v4l2_ctrl_handler_setup(&wpf->ctrls); |
378 | ||
26e0ca22 LP |
379 | return wpf; |
380 | ||
1499be67 LP |
381 | error: |
382 | vsp1_entity_destroy(&wpf->entity); | |
26e0ca22 LP |
383 | return ERR_PTR(ret); |
384 | } |