Commit | Line | Data |
---|---|---|
26e0ca22 LP |
1 | /* |
2 | * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters | |
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 <media/v4l2-subdev.h> | |
15 | ||
16 | #include "vsp1.h" | |
17 | #include "vsp1_rwpf.h" | |
18 | #include "vsp1_video.h" | |
19 | ||
20 | #define RWPF_MIN_WIDTH 1 | |
21 | #define RWPF_MIN_HEIGHT 1 | |
22 | ||
c6c8efb6 LP |
23 | struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, |
24 | struct v4l2_subdev_pad_config *config) | |
25 | { | |
26 | return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, | |
27 | RWPF_PAD_SINK); | |
28 | } | |
29 | ||
26e0ca22 LP |
30 | /* ----------------------------------------------------------------------------- |
31 | * V4L2 Subdevice Pad Operations | |
32 | */ | |
33 | ||
c6c8efb6 LP |
34 | static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, |
35 | struct v4l2_subdev_pad_config *cfg, | |
36 | struct v4l2_subdev_mbus_code_enum *code) | |
26e0ca22 LP |
37 | { |
38 | static const unsigned int codes[] = { | |
27ffaeb0 BB |
39 | MEDIA_BUS_FMT_ARGB8888_1X32, |
40 | MEDIA_BUS_FMT_AYUV8_1X32, | |
26e0ca22 LP |
41 | }; |
42 | ||
43 | if (code->index >= ARRAY_SIZE(codes)) | |
44 | return -EINVAL; | |
45 | ||
46 | code->code = codes[code->index]; | |
47 | ||
48 | return 0; | |
49 | } | |
50 | ||
c6c8efb6 LP |
51 | static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, |
52 | struct v4l2_subdev_pad_config *cfg, | |
53 | struct v4l2_subdev_frame_size_enum *fse) | |
26e0ca22 LP |
54 | { |
55 | struct vsp1_rwpf *rwpf = to_rwpf(subdev); | |
26e0ca22 | 56 | |
076e834f LP |
57 | return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH, |
58 | RWPF_MIN_HEIGHT, rwpf->max_width, | |
59 | rwpf->max_height); | |
26e0ca22 LP |
60 | } |
61 | ||
c6c8efb6 LP |
62 | static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, |
63 | struct v4l2_subdev_pad_config *cfg, | |
64 | struct v4l2_subdev_format *fmt) | |
26e0ca22 LP |
65 | { |
66 | struct vsp1_rwpf *rwpf = to_rwpf(subdev); | |
e790c3cb | 67 | struct v4l2_subdev_pad_config *config; |
26e0ca22 | 68 | struct v4l2_mbus_framefmt *format; |
e5ad37b6 | 69 | struct v4l2_rect *crop; |
26e0ca22 | 70 | |
e790c3cb LP |
71 | config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); |
72 | if (!config) | |
73 | return -EINVAL; | |
74 | ||
26e0ca22 | 75 | /* Default to YUV if the requested format is not supported. */ |
27ffaeb0 BB |
76 | if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && |
77 | fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) | |
78 | fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; | |
26e0ca22 | 79 | |
e790c3cb | 80 | format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); |
26e0ca22 LP |
81 | |
82 | if (fmt->pad == RWPF_PAD_SOURCE) { | |
83 | /* The RWPF performs format conversion but can't scale, only the | |
84 | * format code can be changed on the source pad. | |
85 | */ | |
86 | format->code = fmt->format.code; | |
87 | fmt->format = *format; | |
88 | return 0; | |
89 | } | |
90 | ||
91 | format->code = fmt->format.code; | |
92 | format->width = clamp_t(unsigned int, fmt->format.width, | |
93 | RWPF_MIN_WIDTH, rwpf->max_width); | |
94 | format->height = clamp_t(unsigned int, fmt->format.height, | |
95 | RWPF_MIN_HEIGHT, rwpf->max_height); | |
96 | format->field = V4L2_FIELD_NONE; | |
97 | format->colorspace = V4L2_COLORSPACE_SRGB; | |
98 | ||
99 | fmt->format = *format; | |
100 | ||
e5ad37b6 | 101 | /* Update the sink crop rectangle. */ |
b7e5107e | 102 | crop = vsp1_rwpf_get_crop(rwpf, config); |
e5ad37b6 LP |
103 | crop->left = 0; |
104 | crop->top = 0; | |
105 | crop->width = fmt->format.width; | |
106 | crop->height = fmt->format.height; | |
107 | ||
26e0ca22 | 108 | /* Propagate the format to the source pad. */ |
e790c3cb LP |
109 | format = vsp1_entity_get_pad_format(&rwpf->entity, config, |
110 | RWPF_PAD_SOURCE); | |
26e0ca22 LP |
111 | *format = fmt->format; |
112 | ||
113 | return 0; | |
114 | } | |
e5ad37b6 | 115 | |
c6c8efb6 LP |
116 | static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, |
117 | struct v4l2_subdev_pad_config *cfg, | |
118 | struct v4l2_subdev_selection *sel) | |
e5ad37b6 LP |
119 | { |
120 | struct vsp1_rwpf *rwpf = to_rwpf(subdev); | |
e790c3cb | 121 | struct v4l2_subdev_pad_config *config; |
e5ad37b6 LP |
122 | struct v4l2_mbus_framefmt *format; |
123 | ||
124 | /* Cropping is implemented on the sink pad. */ | |
125 | if (sel->pad != RWPF_PAD_SINK) | |
126 | return -EINVAL; | |
127 | ||
e790c3cb LP |
128 | config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); |
129 | if (!config) | |
130 | return -EINVAL; | |
131 | ||
e5ad37b6 LP |
132 | switch (sel->target) { |
133 | case V4L2_SEL_TGT_CROP: | |
b7e5107e | 134 | sel->r = *vsp1_rwpf_get_crop(rwpf, config); |
e5ad37b6 LP |
135 | break; |
136 | ||
137 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
e790c3cb LP |
138 | format = vsp1_entity_get_pad_format(&rwpf->entity, config, |
139 | RWPF_PAD_SINK); | |
e5ad37b6 LP |
140 | sel->r.left = 0; |
141 | sel->r.top = 0; | |
142 | sel->r.width = format->width; | |
143 | sel->r.height = format->height; | |
144 | break; | |
145 | ||
146 | default: | |
147 | return -EINVAL; | |
148 | } | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
c6c8efb6 LP |
153 | static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, |
154 | struct v4l2_subdev_pad_config *cfg, | |
155 | struct v4l2_subdev_selection *sel) | |
e5ad37b6 LP |
156 | { |
157 | struct vsp1_rwpf *rwpf = to_rwpf(subdev); | |
e790c3cb | 158 | struct v4l2_subdev_pad_config *config; |
e5ad37b6 LP |
159 | struct v4l2_mbus_framefmt *format; |
160 | struct v4l2_rect *crop; | |
161 | ||
162 | /* Cropping is implemented on the sink pad. */ | |
163 | if (sel->pad != RWPF_PAD_SINK) | |
164 | return -EINVAL; | |
165 | ||
166 | if (sel->target != V4L2_SEL_TGT_CROP) | |
167 | return -EINVAL; | |
168 | ||
e790c3cb LP |
169 | config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); |
170 | if (!config) | |
171 | return -EINVAL; | |
172 | ||
e5ad37b6 LP |
173 | /* Make sure the crop rectangle is entirely contained in the image. The |
174 | * WPF top and left offsets are limited to 255. | |
175 | */ | |
e790c3cb LP |
176 | format = vsp1_entity_get_pad_format(&rwpf->entity, config, |
177 | RWPF_PAD_SINK); | |
85a0638b DHG |
178 | |
179 | /* Restrict the crop rectangle coordinates to multiples of 2 to avoid | |
180 | * shifting the color plane. | |
181 | */ | |
182 | if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { | |
183 | sel->r.left = ALIGN(sel->r.left, 2); | |
184 | sel->r.top = ALIGN(sel->r.top, 2); | |
185 | sel->r.width = round_down(sel->r.width, 2); | |
186 | sel->r.height = round_down(sel->r.height, 2); | |
187 | } | |
188 | ||
e5ad37b6 LP |
189 | sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); |
190 | sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); | |
191 | if (rwpf->entity.type == VSP1_ENTITY_WPF) { | |
192 | sel->r.left = min_t(unsigned int, sel->r.left, 255); | |
193 | sel->r.top = min_t(unsigned int, sel->r.top, 255); | |
194 | } | |
195 | sel->r.width = min_t(unsigned int, sel->r.width, | |
196 | format->width - sel->r.left); | |
197 | sel->r.height = min_t(unsigned int, sel->r.height, | |
198 | format->height - sel->r.top); | |
199 | ||
b7e5107e | 200 | crop = vsp1_rwpf_get_crop(rwpf, config); |
e5ad37b6 LP |
201 | *crop = sel->r; |
202 | ||
203 | /* Propagate the format to the source pad. */ | |
e790c3cb LP |
204 | format = vsp1_entity_get_pad_format(&rwpf->entity, config, |
205 | RWPF_PAD_SOURCE); | |
e5ad37b6 LP |
206 | format->width = crop->width; |
207 | format->height = crop->height; | |
208 | ||
209 | return 0; | |
210 | } | |
bd2fdd5a | 211 | |
c6c8efb6 LP |
212 | const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { |
213 | .init_cfg = vsp1_entity_init_cfg, | |
214 | .enum_mbus_code = vsp1_rwpf_enum_mbus_code, | |
215 | .enum_frame_size = vsp1_rwpf_enum_frame_size, | |
3f557220 | 216 | .get_fmt = vsp1_subdev_get_pad_format, |
c6c8efb6 LP |
217 | .set_fmt = vsp1_rwpf_set_format, |
218 | .get_selection = vsp1_rwpf_get_selection, | |
219 | .set_selection = vsp1_rwpf_set_selection, | |
220 | }; | |
221 | ||
bd2fdd5a LP |
222 | /* ----------------------------------------------------------------------------- |
223 | * Controls | |
224 | */ | |
225 | ||
226 | static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl) | |
227 | { | |
228 | struct vsp1_rwpf *rwpf = | |
229 | container_of(ctrl->handler, struct vsp1_rwpf, ctrls); | |
230 | ||
231 | switch (ctrl->id) { | |
232 | case V4L2_CID_ALPHA_COMPONENT: | |
233 | rwpf->alpha = ctrl->val; | |
234 | break; | |
235 | } | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { | |
241 | .s_ctrl = vsp1_rwpf_s_ctrl, | |
242 | }; | |
243 | ||
894dde5c | 244 | int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) |
bd2fdd5a | 245 | { |
894dde5c | 246 | v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); |
bd2fdd5a LP |
247 | v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, |
248 | V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); | |
249 | ||
250 | rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls; | |
251 | ||
252 | return rwpf->ctrls.error; | |
253 | } |