]>
Commit | Line | Data |
---|---|---|
26e0ca22 LP |
1 | /* |
2 | * vsp1_rpf.c -- R-Car VSP1 Read 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" | |
5e8dbbf3 | 19 | #include "vsp1_dl.h" |
a0cdac56 | 20 | #include "vsp1_pipe.h" |
26e0ca22 LP |
21 | #include "vsp1_rwpf.h" |
22 | #include "vsp1_video.h" | |
23 | ||
24 | #define RPF_MAX_WIDTH 8190 | |
25 | #define RPF_MAX_HEIGHT 8190 | |
26 | ||
27 | /* ----------------------------------------------------------------------------- | |
28 | * Device Access | |
29 | */ | |
30 | ||
5e8dbbf3 LP |
31 | static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, |
32 | struct vsp1_dl_list *dl, u32 reg, u32 data) | |
26e0ca22 | 33 | { |
5e8dbbf3 | 34 | vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data); |
26e0ca22 LP |
35 | } |
36 | ||
37 | /* ----------------------------------------------------------------------------- | |
7b905f05 | 38 | * V4L2 Subdevice Operations |
26e0ca22 LP |
39 | */ |
40 | ||
eb9163d3 | 41 | static const struct v4l2_subdev_ops rpf_ops = { |
c6c8efb6 | 42 | .pad = &vsp1_rwpf_pad_ops, |
7b905f05 LP |
43 | }; |
44 | ||
45 | /* ----------------------------------------------------------------------------- | |
46 | * VSP1 Entity Operations | |
47 | */ | |
48 | ||
5e8dbbf3 | 49 | static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) |
26e0ca22 | 50 | { |
7b905f05 LP |
51 | struct vsp1_rwpf *rpf = entity_to_rwpf(entity); |
52 | ||
5e8dbbf3 | 53 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, |
7b905f05 | 54 | rpf->mem.addr[0] + rpf->offsets[0]); |
5e8dbbf3 | 55 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, |
7b905f05 | 56 | rpf->mem.addr[1] + rpf->offsets[1]); |
5e8dbbf3 | 57 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, |
7b905f05 LP |
58 | rpf->mem.addr[2] + rpf->offsets[1]); |
59 | } | |
60 | ||
83dd019d LP |
61 | static void rpf_configure(struct vsp1_entity *entity, |
62 | struct vsp1_pipeline *pipe, | |
fc845e52 | 63 | struct vsp1_dl_list *dl, bool full) |
7b905f05 | 64 | { |
7b905f05 | 65 | struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); |
86960eec LP |
66 | const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; |
67 | const struct v4l2_pix_format_mplane *format = &rpf->format; | |
e790c3cb LP |
68 | const struct v4l2_mbus_framefmt *source_format; |
69 | const struct v4l2_mbus_framefmt *sink_format; | |
b7e5107e LP |
70 | const struct v4l2_rect *crop; |
71 | unsigned int left = 0; | |
72 | unsigned int top = 0; | |
26e0ca22 LP |
73 | u32 pstride; |
74 | u32 infmt; | |
7578c204 | 75 | |
d05a3310 LP |
76 | if (!full) { |
77 | vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, | |
78 | rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); | |
79 | vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | | |
80 | (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); | |
81 | ||
82 | vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); | |
fc845e52 | 83 | return; |
d05a3310 | 84 | } |
fc845e52 | 85 | |
e5ad37b6 LP |
86 | /* Source size, stride and crop offsets. |
87 | * | |
88 | * The crop offsets correspond to the location of the crop rectangle top | |
89 | * left corner in the plane buffer. Only two offsets are needed, as | |
90 | * planes 2 and 3 always have identical strides. | |
91 | */ | |
b7e5107e LP |
92 | crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); |
93 | ||
5e8dbbf3 | 94 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, |
e5ad37b6 LP |
95 | (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | |
96 | (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); | |
5e8dbbf3 | 97 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, |
e5ad37b6 LP |
98 | (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | |
99 | (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); | |
26e0ca22 | 100 | |
e5ad37b6 LP |
101 | rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline |
102 | + crop->left * fmtinfo->bpp[0] / 8; | |
26e0ca22 LP |
103 | pstride = format->plane_fmt[0].bytesperline |
104 | << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; | |
857161fc | 105 | |
e5ad37b6 LP |
106 | if (format->num_planes > 1) { |
107 | rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline | |
abe9609f LP |
108 | + crop->left / fmtinfo->hsub * fmtinfo->bpp[1] |
109 | / 8; | |
26e0ca22 LP |
110 | pstride |= format->plane_fmt[1].bytesperline |
111 | << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; | |
4d346be5 LP |
112 | } else { |
113 | rpf->offsets[1] = 0; | |
e5ad37b6 | 114 | } |
26e0ca22 | 115 | |
5e8dbbf3 | 116 | vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); |
26e0ca22 LP |
117 | |
118 | /* Format */ | |
e790c3cb LP |
119 | sink_format = vsp1_entity_get_pad_format(&rpf->entity, |
120 | rpf->entity.config, | |
121 | RWPF_PAD_SINK); | |
122 | source_format = vsp1_entity_get_pad_format(&rpf->entity, | |
123 | rpf->entity.config, | |
124 | RWPF_PAD_SOURCE); | |
125 | ||
26e0ca22 LP |
126 | infmt = VI6_RPF_INFMT_CIPM |
127 | | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); | |
128 | ||
129 | if (fmtinfo->swap_yc) | |
130 | infmt |= VI6_RPF_INFMT_SPYCS; | |
131 | if (fmtinfo->swap_uv) | |
132 | infmt |= VI6_RPF_INFMT_SPUVS; | |
133 | ||
e790c3cb | 134 | if (sink_format->code != source_format->code) |
26e0ca22 LP |
135 | infmt |= VI6_RPF_INFMT_CSC; |
136 | ||
5e8dbbf3 LP |
137 | vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt); |
138 | vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap); | |
26e0ca22 | 139 | |
629bb6d4 | 140 | /* Output location */ |
b7e5107e LP |
141 | if (pipe->bru) { |
142 | const struct v4l2_rect *compose; | |
143 | ||
ccd3d95a LP |
144 | compose = vsp1_entity_get_pad_selection(pipe->bru, |
145 | pipe->bru->config, | |
146 | rpf->bru_input, | |
147 | V4L2_SEL_TGT_COMPOSE); | |
b7e5107e LP |
148 | left = compose->left; |
149 | top = compose->top; | |
150 | } | |
151 | ||
5e8dbbf3 | 152 | vsp1_rpf_write(rpf, dl, VI6_RPF_LOC, |
b7e5107e LP |
153 | (left << VI6_RPF_LOC_HCOORD_SHIFT) | |
154 | (top << VI6_RPF_LOC_VCOORD_SHIFT)); | |
26e0ca22 | 155 | |
30276a73 LP |
156 | /* On Gen2 use the alpha channel (extended to 8 bits) when available or |
157 | * a fixed alpha value set through the V4L2_CID_ALPHA_COMPONENT control | |
158 | * otherwise. | |
159 | * | |
160 | * The Gen3 RPF has extended alpha capability and can both multiply the | |
161 | * alpha channel by a fixed global alpha value, and multiply the pixel | |
162 | * components to convert the input to premultiplied alpha. | |
163 | * | |
164 | * As alpha premultiplication is available in the BRU for both Gen2 and | |
165 | * Gen3 we handle it there and use the Gen3 alpha multiplier for global | |
166 | * alpha multiplication only. This however prevents conversion to | |
167 | * premultiplied alpha if no BRU is present in the pipeline. If that use | |
168 | * case turns out to be useful we will revisit the implementation (for | |
169 | * Gen3 only). | |
170 | * | |
171 | * We enable alpha multiplication on Gen3 using the fixed alpha value | |
172 | * set through the V4L2_CID_ALPHA_COMPONENT control when the input | |
173 | * contains an alpha channel. On Gen2 the global alpha is ignored in | |
174 | * that case. | |
175 | * | |
176 | * In all cases, disable color keying. | |
26e0ca22 | 177 | */ |
5e8dbbf3 | 178 | vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | |
7a52b6de LP |
179 | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED |
180 | : VI6_RPF_ALPH_SEL_ASEL_FIXED)); | |
3dbb6100 | 181 | |
30276a73 LP |
182 | if (entity->vsp1->info->gen == 3) { |
183 | u32 mult; | |
184 | ||
185 | if (fmtinfo->alpha) { | |
186 | /* When the input contains an alpha channel enable the | |
187 | * alpha multiplier. If the input is premultiplied we | |
188 | * need to multiply both the alpha channel and the pixel | |
189 | * components by the global alpha value to keep them | |
190 | * premultiplied. Otherwise multiply the alpha channel | |
191 | * only. | |
192 | */ | |
193 | bool premultiplied = format->flags | |
194 | & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; | |
195 | ||
196 | mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO | |
197 | | (premultiplied ? | |
198 | VI6_RPF_MULT_ALPHA_P_MMD_RATIO : | |
d05a3310 | 199 | VI6_RPF_MULT_ALPHA_P_MMD_NONE); |
30276a73 LP |
200 | } else { |
201 | /* When the input doesn't contain an alpha channel the | |
202 | * global alpha value is applied in the unpacking unit, | |
203 | * the alpha multiplier isn't needed and must be | |
204 | * disabled. | |
205 | */ | |
206 | mult = VI6_RPF_MULT_ALPHA_A_MMD_NONE | |
207 | | VI6_RPF_MULT_ALPHA_P_MMD_NONE; | |
208 | } | |
209 | ||
d05a3310 | 210 | rpf->mult_alpha = mult; |
30276a73 LP |
211 | } |
212 | ||
5e8dbbf3 LP |
213 | vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); |
214 | vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); | |
30276a73 | 215 | |
26e0ca22 LP |
216 | } |
217 | ||
52434534 | 218 | static const struct vsp1_entity_operations rpf_entity_ops = { |
b58faa95 | 219 | .set_memory = rpf_set_memory, |
7b905f05 | 220 | .configure = rpf_configure, |
26e0ca22 LP |
221 | }; |
222 | ||
223 | /* ----------------------------------------------------------------------------- | |
224 | * Initialization and Cleanup | |
225 | */ | |
226 | ||
227 | struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) | |
228 | { | |
26e0ca22 | 229 | struct vsp1_rwpf *rpf; |
823329df | 230 | char name[6]; |
26e0ca22 LP |
231 | int ret; |
232 | ||
233 | rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); | |
234 | if (rpf == NULL) | |
235 | return ERR_PTR(-ENOMEM); | |
236 | ||
237 | rpf->max_width = RPF_MAX_WIDTH; | |
238 | rpf->max_height = RPF_MAX_HEIGHT; | |
239 | ||
52434534 | 240 | rpf->entity.ops = &rpf_entity_ops; |
26e0ca22 LP |
241 | rpf->entity.type = VSP1_ENTITY_RPF; |
242 | rpf->entity.index = index; | |
26e0ca22 | 243 | |
823329df | 244 | sprintf(name, "rpf.%u", index); |
6a8e07b2 LP |
245 | ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops, |
246 | MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); | |
26e0ca22 LP |
247 | if (ret < 0) |
248 | return ERR_PTR(ret); | |
249 | ||
7578c204 | 250 | /* Initialize the control handler. */ |
894dde5c | 251 | ret = vsp1_rwpf_init_ctrls(rpf, 0); |
bd2fdd5a | 252 | if (ret < 0) { |
7578c204 LP |
253 | dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n", |
254 | index); | |
7578c204 LP |
255 | goto error; |
256 | } | |
257 | ||
d05a3310 LP |
258 | v4l2_ctrl_handler_setup(&rpf->ctrls); |
259 | ||
26e0ca22 LP |
260 | return rpf; |
261 | ||
1499be67 LP |
262 | error: |
263 | vsp1_entity_destroy(&rpf->entity); | |
26e0ca22 LP |
264 | return ERR_PTR(ret); |
265 | } |