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