1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
4 *
5 * Copyright (C) 2013-2014 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10 #include <linux/device.h>
11
12 #include <media/v4l2-subdev.h>
13
14 #include "vsp1.h"
15 #include "vsp1_dl.h"
16 #include "vsp1_pipe.h"
17 #include "vsp1_rwpf.h"
18 #include "vsp1_video.h"
19
20 #define RPF_MAX_WIDTH 8190
21 #define RPF_MAX_HEIGHT 8190
22
23 /* Pre extended display list command data structure. */
24 struct vsp1_extcmd_auto_fld_body {
25 u32 top_y0;
26 u32 bottom_y0;
27 u32 top_c0;
28 u32 bottom_c0;
29 u32 top_c1;
30 u32 bottom_c1;
31 u32 reserved0;
32 u32 reserved1;
33 } __packed;
34
35 /* -----------------------------------------------------------------------------
36 * Device Access
37 */
38
vsp1_rpf_write(struct vsp1_rwpf * rpf,struct vsp1_dl_body * dlb,u32 reg,u32 data)39 static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
40 struct vsp1_dl_body *dlb, u32 reg, u32 data)
41 {
42 vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET,
43 data);
44 }
45
46 /* -----------------------------------------------------------------------------
47 * VSP1 Entity Operations
48 */
49
rpf_configure_stream(struct vsp1_entity * entity,struct v4l2_subdev_state * state,struct vsp1_pipeline * pipe,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)50 static void rpf_configure_stream(struct vsp1_entity *entity,
51 struct v4l2_subdev_state *state,
52 struct vsp1_pipeline *pipe,
53 struct vsp1_dl_list *dl,
54 struct vsp1_dl_body *dlb)
55 {
56 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
57 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
58 const struct v4l2_pix_format_mplane *format = &rpf->format;
59 const struct v4l2_mbus_framefmt *source_format;
60 const struct v4l2_mbus_framefmt *sink_format;
61 unsigned int left = 0;
62 unsigned int top = 0;
63 u32 pstride;
64 u32 infmt;
65
66 /* Stride */
67 pstride = format->plane_fmt[0].bytesperline
68 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
69 if (format->num_planes > 1)
70 pstride |= format->plane_fmt[1].bytesperline
71 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
72
73 /*
74 * pstride has both STRIDE_Y and STRIDE_C, but multiplying the whole
75 * of pstride by 2 is conveniently OK here as we are multiplying both
76 * values.
77 */
78 if (pipe->interlaced)
79 pstride *= 2;
80
81 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
82
83 /* Format */
84 sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
85 source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
86
87 infmt = VI6_RPF_INFMT_CIPM
88 | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
89
90 if (fmtinfo->swap_yc)
91 infmt |= VI6_RPF_INFMT_SPYCS;
92 if (fmtinfo->swap_uv)
93 infmt |= VI6_RPF_INFMT_SPUVS;
94
95 if (sink_format->code != source_format->code)
96 infmt |= VI6_RPF_INFMT_CSC;
97
98 vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);
99 vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);
100
101 if (entity->vsp1->info->gen == 4) {
102 u32 ext_infmt0;
103 u32 ext_infmt1;
104 u32 ext_infmt2;
105
106 switch (fmtinfo->fourcc) {
107 case V4L2_PIX_FMT_RGBX1010102:
108 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;
109 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(0, 10, 20, 0);
110 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 0);
111 break;
112
113 case V4L2_PIX_FMT_RGBA1010102:
114 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;
115 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(0, 10, 20, 30);
116 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 2);
117 break;
118
119 case V4L2_PIX_FMT_ARGB2101010:
120 ext_infmt0 = VI6_RPF_EXT_INFMT0_BYPP_M1_RGB10;
121 ext_infmt1 = VI6_RPF_EXT_INFMT1_PACK_CPOS(2, 12, 22, 0);
122 ext_infmt2 = VI6_RPF_EXT_INFMT2_PACK_CLEN(10, 10, 10, 2);
123 break;
124
125 case V4L2_PIX_FMT_Y210:
126 ext_infmt0 = VI6_RPF_EXT_INFMT0_F2B |
127 VI6_RPF_EXT_INFMT0_IPBD_Y_10 |
128 VI6_RPF_EXT_INFMT0_IPBD_C_10;
129 ext_infmt1 = 0x0;
130 ext_infmt2 = 0x0;
131 break;
132
133 case V4L2_PIX_FMT_Y212:
134 ext_infmt0 = VI6_RPF_EXT_INFMT0_F2B |
135 VI6_RPF_EXT_INFMT0_IPBD_Y_12 |
136 VI6_RPF_EXT_INFMT0_IPBD_C_12;
137 ext_infmt1 = 0x0;
138 ext_infmt2 = 0x0;
139 break;
140
141 default:
142 ext_infmt0 = 0;
143 ext_infmt1 = 0;
144 ext_infmt2 = 0;
145 break;
146 }
147
148 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT0, ext_infmt0);
149 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT1, ext_infmt1);
150 vsp1_rpf_write(rpf, dlb, VI6_RPF_EXT_INFMT2, ext_infmt2);
151 }
152
153 /* Output location. */
154 if (pipe->brx) {
155 const struct v4l2_rect *compose;
156
157 compose = v4l2_subdev_state_get_compose(pipe->brx->state,
158 rpf->brx_input);
159 left = compose->left;
160 top = compose->top;
161 }
162
163 if (pipe->interlaced)
164 top /= 2;
165
166 vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
167 (left << VI6_RPF_LOC_HCOORD_SHIFT) |
168 (top << VI6_RPF_LOC_VCOORD_SHIFT));
169
170 /*
171 * On Gen2 use the alpha channel (extended to 8 bits) when available or
172 * a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control
173 * otherwise.
174 *
175 * The Gen3+ RPF has extended alpha capability and can both multiply the
176 * alpha channel by a fixed global alpha value, and multiply the pixel
177 * components to convert the input to premultiplied alpha.
178 *
179 * As alpha premultiplication is available in the BRx for both Gen2 and
180 * Gen3+ we handle it there and use the Gen3 alpha multiplier for global
181 * alpha multiplication only. This however prevents conversion to
182 * premultiplied alpha if no BRx is present in the pipeline. If that use
183 * case turns out to be useful we will revisit the implementation (for
184 * Gen3 only).
185 *
186 * We enable alpha multiplication on Gen3+ using the fixed alpha value
187 * set through the V4L2_CID_ALPHA_COMPONENT control when the input
188 * contains an alpha channel. On Gen2 the global alpha is ignored in
189 * that case.
190 *
191 * In all cases, disable color keying.
192 */
193 vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
194 (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
195 : VI6_RPF_ALPH_SEL_ASEL_FIXED));
196
197 if (entity->vsp1->info->gen >= 3) {
198 u32 mult;
199
200 if (fmtinfo->alpha) {
201 /*
202 * When the input contains an alpha channel enable the
203 * alpha multiplier. If the input is premultiplied we
204 * need to multiply both the alpha channel and the pixel
205 * components by the global alpha value to keep them
206 * premultiplied. Otherwise multiply the alpha channel
207 * only.
208 */
209 bool premultiplied = format->flags
210 & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
211
212 mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO
213 | (premultiplied ?
214 VI6_RPF_MULT_ALPHA_P_MMD_RATIO :
215 VI6_RPF_MULT_ALPHA_P_MMD_NONE);
216 } else {
217 /*
218 * When the input doesn't contain an alpha channel the
219 * global alpha value is applied in the unpacking unit,
220 * the alpha multiplier isn't needed and must be
221 * disabled.
222 */
223 mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE
224 | VI6_RPF_MULT_ALPHA_P_MMD_NONE;
225 }
226
227 rpf->mult_alpha = mult;
228 }
229
230 vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0);
231 vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0);
232
233 }
234
vsp1_rpf_configure_autofld(struct vsp1_rwpf * rpf,struct vsp1_dl_list * dl)235 static void vsp1_rpf_configure_autofld(struct vsp1_rwpf *rpf,
236 struct vsp1_dl_list *dl)
237 {
238 const struct v4l2_pix_format_mplane *format = &rpf->format;
239 struct vsp1_dl_ext_cmd *cmd;
240 struct vsp1_extcmd_auto_fld_body *auto_fld;
241 u32 offset_y, offset_c;
242
243 cmd = vsp1_dl_get_pre_cmd(dl);
244 if (WARN_ONCE(!cmd, "Failed to obtain an autofld cmd"))
245 return;
246
247 /* Re-index our auto_fld to match the current RPF. */
248 auto_fld = cmd->data;
249 auto_fld = &auto_fld[rpf->entity.index];
250
251 auto_fld->top_y0 = rpf->mem.addr[0];
252 auto_fld->top_c0 = rpf->mem.addr[1];
253 auto_fld->top_c1 = rpf->mem.addr[2];
254
255 offset_y = format->plane_fmt[0].bytesperline;
256 offset_c = format->plane_fmt[1].bytesperline;
257
258 auto_fld->bottom_y0 = rpf->mem.addr[0] + offset_y;
259 auto_fld->bottom_c0 = rpf->mem.addr[1] + offset_c;
260 auto_fld->bottom_c1 = rpf->mem.addr[2] + offset_c;
261
262 cmd->flags |= VI6_DL_EXT_AUTOFLD_INT | BIT(16 + rpf->entity.index);
263 }
264
rpf_configure_frame(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)265 static void rpf_configure_frame(struct vsp1_entity *entity,
266 struct vsp1_pipeline *pipe,
267 struct vsp1_dl_list *dl,
268 struct vsp1_dl_body *dlb)
269 {
270 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
271
272 vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET,
273 rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
274 vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
275 (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
276
277 vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha);
278 }
279
rpf_configure_partition(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,const struct vsp1_partition * partition,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)280 static void rpf_configure_partition(struct vsp1_entity *entity,
281 struct vsp1_pipeline *pipe,
282 const struct vsp1_partition *partition,
283 struct vsp1_dl_list *dl,
284 struct vsp1_dl_body *dlb)
285 {
286 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
287 struct vsp1_rwpf_memory mem = rpf->mem;
288 struct vsp1_device *vsp1 = rpf->entity.vsp1;
289 const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
290 const struct v4l2_pix_format_mplane *format = &rpf->format;
291 struct v4l2_rect crop = partition->rpf[rpf->entity.index];
292
293 /*
294 * Source size and crop offsets.
295 *
296 * The crop offsets correspond to the location of the crop
297 * rectangle top left corner in the plane buffer. Only two
298 * offsets are needed, as planes 2 and 3 always have identical
299 * strides.
300 */
301
302 if (pipe->interlaced) {
303 crop.height = round_down(crop.height / 2, fmtinfo->vsub);
304 crop.top = round_down(crop.top / 2, fmtinfo->vsub);
305 }
306
307 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,
308 (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
309 (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
310 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,
311 (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
312 (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
313
314 mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
315 + crop.left * fmtinfo->bpp[0] / 8;
316
317 if (format->num_planes > 1) {
318 unsigned int bpl = format->plane_fmt[1].bytesperline;
319 unsigned int offset;
320
321 offset = crop.top / fmtinfo->vsub * bpl
322 + crop.left / fmtinfo->hsub * fmtinfo->bpp[1] / 8;
323 mem.addr[1] += offset;
324 mem.addr[2] += offset;
325 }
326
327 /*
328 * On Gen3+ hardware the SPUVS bit has no effect on 3-planar
329 * formats. Swap the U and V planes manually in that case.
330 */
331 if (vsp1->info->gen >= 3 && format->num_planes == 3 &&
332 fmtinfo->swap_uv)
333 swap(mem.addr[1], mem.addr[2]);
334
335 /*
336 * Interlaced pipelines will use the extended pre-cmd to process
337 * SRCM_ADDR_{Y,C0,C1}.
338 */
339 if (pipe->interlaced) {
340 vsp1_rpf_configure_autofld(rpf, dl);
341 } else {
342 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
343 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
344 vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
345 }
346 }
347
rpf_partition(struct vsp1_entity * entity,struct v4l2_subdev_state * state,struct vsp1_pipeline * pipe,struct vsp1_partition * partition,unsigned int partition_idx,struct v4l2_rect * window)348 static void rpf_partition(struct vsp1_entity *entity,
349 struct v4l2_subdev_state *state,
350 struct vsp1_pipeline *pipe,
351 struct vsp1_partition *partition,
352 unsigned int partition_idx,
353 struct v4l2_rect *window)
354 {
355 struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
356 struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index];
357
358 /*
359 * Partition Algorithm Control
360 *
361 * The partition algorithm can split this frame into multiple slices. We
362 * must adjust our partition window based on the pipe configuration to
363 * match the destination partition window. To achieve this, we adjust
364 * our crop to provide a 'sub-crop' matching the expected partition
365 * window.
366 */
367 *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
368
369 if (pipe->partitions > 1) {
370 rpf_rect->width = window->width;
371 rpf_rect->left += window->left;
372 }
373 }
374
375 static const struct vsp1_entity_operations rpf_entity_ops = {
376 .configure_stream = rpf_configure_stream,
377 .configure_frame = rpf_configure_frame,
378 .configure_partition = rpf_configure_partition,
379 .partition = rpf_partition,
380 };
381
382 /* -----------------------------------------------------------------------------
383 * Initialization and Cleanup
384 */
385
vsp1_rpf_create(struct vsp1_device * vsp1,unsigned int index)386 struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
387 {
388 struct vsp1_rwpf *rpf;
389 char name[6];
390 int ret;
391
392 rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
393 if (rpf == NULL)
394 return ERR_PTR(-ENOMEM);
395
396 rpf->max_width = RPF_MAX_WIDTH;
397 rpf->max_height = RPF_MAX_HEIGHT;
398
399 rpf->entity.ops = &rpf_entity_ops;
400 rpf->entity.type = VSP1_ENTITY_RPF;
401 rpf->entity.index = index;
402
403 sprintf(name, "rpf.%u", index);
404 ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &vsp1_rwpf_subdev_ops,
405 MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
406 if (ret < 0)
407 return ERR_PTR(ret);
408
409 /* Initialize the control handler. */
410 ret = vsp1_rwpf_init_ctrls(rpf, 0);
411 if (ret < 0) {
412 dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
413 index);
414 goto error;
415 }
416
417 v4l2_ctrl_handler_setup(&rpf->ctrls);
418
419 return rpf;
420
421 error:
422 vsp1_entity_destroy(&rpf->entity);
423 return ERR_PTR(ret);
424 }
425