diff options
author | Tomasz Stanislawski <t.stanislaws@samsung.com> | 2011-08-25 06:14:26 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-01-10 20:19:23 -0500 |
commit | 0d066d3f6fdfd189bef5fe653477f5f9db2eb225 (patch) | |
tree | 34b4e8b099292ea346692141f330199f17a181d9 /drivers | |
parent | 992efeff79fe8de44d4e8b7636bb2e5d9dcf698d (diff) |
[media] v4l: s5p-tv: mixer: add support for selection API
This patch add support for V4L2 selection API to s5p-tv driver. Moreover it
removes old API for cropping. Old applications would still work because the
crop ioctls are emulated using the selection API.
Signed-off-by: Tomasz Stanislawski <t.stanislaws@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/s5p-tv/mixer.h | 14 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_grp_layer.c | 157 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_video.c | 342 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_vp_layer.c | 108 |
4 files changed, 425 insertions, 196 deletions
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h index 51ad59b30358..1597078c4a50 100644 --- a/drivers/media/video/s5p-tv/mixer.h +++ b/drivers/media/video/s5p-tv/mixer.h | |||
@@ -86,6 +86,17 @@ struct mxr_crop { | |||
86 | unsigned int field; | 86 | unsigned int field; |
87 | }; | 87 | }; |
88 | 88 | ||
89 | /** stages of geometry operations */ | ||
90 | enum mxr_geometry_stage { | ||
91 | MXR_GEOMETRY_SINK, | ||
92 | MXR_GEOMETRY_COMPOSE, | ||
93 | MXR_GEOMETRY_CROP, | ||
94 | MXR_GEOMETRY_SOURCE, | ||
95 | }; | ||
96 | |||
97 | /* flag indicating that offset should be 0 */ | ||
98 | #define MXR_NO_OFFSET 0x80000000 | ||
99 | |||
89 | /** description of transformation from source to destination image */ | 100 | /** description of transformation from source to destination image */ |
90 | struct mxr_geometry { | 101 | struct mxr_geometry { |
91 | /** cropping for source image */ | 102 | /** cropping for source image */ |
@@ -133,7 +144,8 @@ struct mxr_layer_ops { | |||
133 | /** streaming stop/start */ | 144 | /** streaming stop/start */ |
134 | void (*stream_set)(struct mxr_layer *, int); | 145 | void (*stream_set)(struct mxr_layer *, int); |
135 | /** adjusting geometry */ | 146 | /** adjusting geometry */ |
136 | void (*fix_geometry)(struct mxr_layer *); | 147 | void (*fix_geometry)(struct mxr_layer *, |
148 | enum mxr_geometry_stage, unsigned long); | ||
137 | }; | 149 | }; |
138 | 150 | ||
139 | /** layer instance, a single window and content displayed on output */ | 151 | /** layer instance, a single window and content displayed on output */ |
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c index de8270c2b6e7..b93a21f5aa13 100644 --- a/drivers/media/video/s5p-tv/mixer_grp_layer.c +++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c | |||
@@ -101,47 +101,132 @@ static void mxr_graph_format_set(struct mxr_layer *layer) | |||
101 | layer->fmt, &layer->geo); | 101 | layer->fmt, &layer->geo); |
102 | } | 102 | } |
103 | 103 | ||
104 | static void mxr_graph_fix_geometry(struct mxr_layer *layer) | 104 | static inline unsigned int closest(unsigned int x, unsigned int a, |
105 | unsigned int b, unsigned long flags) | ||
106 | { | ||
107 | unsigned int mid = (a + b) / 2; | ||
108 | |||
109 | /* choosing closest value with constraints according to table: | ||
110 | * -------------+-----+-----+-----+-------+ | ||
111 | * flags | 0 | LE | GE | LE|GE | | ||
112 | * -------------+-----+-----+-----+-------+ | ||
113 | * x <= a | a | a | a | a | | ||
114 | * a < x <= mid | a | a | b | a | | ||
115 | * mid < x < b | b | a | b | b | | ||
116 | * b <= x | b | b | b | b | | ||
117 | * -------------+-----+-----+-----+-------+ | ||
118 | */ | ||
119 | |||
120 | /* remove all non-constraint flags */ | ||
121 | flags &= V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE; | ||
122 | |||
123 | if (x <= a) | ||
124 | return a; | ||
125 | if (x >= b) | ||
126 | return b; | ||
127 | if (flags == V4L2_SEL_FLAG_LE) | ||
128 | return a; | ||
129 | if (flags == V4L2_SEL_FLAG_GE) | ||
130 | return b; | ||
131 | if (x <= mid) | ||
132 | return a; | ||
133 | return b; | ||
134 | } | ||
135 | |||
136 | static inline unsigned int do_center(unsigned int center, | ||
137 | unsigned int size, unsigned int upper, unsigned int flags) | ||
138 | { | ||
139 | unsigned int lower; | ||
140 | |||
141 | if (flags & MXR_NO_OFFSET) | ||
142 | return 0; | ||
143 | |||
144 | lower = center - min(center, size / 2); | ||
145 | return min(lower, upper - size); | ||
146 | } | ||
147 | |||
148 | static void mxr_graph_fix_geometry(struct mxr_layer *layer, | ||
149 | enum mxr_geometry_stage stage, unsigned long flags) | ||
105 | { | 150 | { |
106 | struct mxr_geometry *geo = &layer->geo; | 151 | struct mxr_geometry *geo = &layer->geo; |
152 | struct mxr_crop *src = &geo->src; | ||
153 | struct mxr_crop *dst = &geo->dst; | ||
154 | unsigned int x_center, y_center; | ||
107 | 155 | ||
108 | /* limit to boundary size */ | 156 | switch (stage) { |
109 | geo->src.full_width = clamp_val(geo->src.full_width, 1, 32767); | ||
110 | geo->src.full_height = clamp_val(geo->src.full_height, 1, 2047); | ||
111 | geo->src.width = clamp_val(geo->src.width, 1, geo->src.full_width); | ||
112 | geo->src.width = min(geo->src.width, 2047U); | ||
113 | /* not possible to crop of Y axis */ | ||
114 | geo->src.y_offset = min(geo->src.y_offset, geo->src.full_height - 1); | ||
115 | geo->src.height = geo->src.full_height - geo->src.y_offset; | ||
116 | /* limitting offset */ | ||
117 | geo->src.x_offset = min(geo->src.x_offset, | ||
118 | geo->src.full_width - geo->src.width); | ||
119 | |||
120 | /* setting position in output */ | ||
121 | geo->dst.width = min(geo->dst.width, geo->dst.full_width); | ||
122 | geo->dst.height = min(geo->dst.height, geo->dst.full_height); | ||
123 | |||
124 | /* Mixer supports only 1x and 2x scaling */ | ||
125 | if (geo->dst.width >= 2 * geo->src.width) { | ||
126 | geo->x_ratio = 1; | ||
127 | geo->dst.width = 2 * geo->src.width; | ||
128 | } else { | ||
129 | geo->x_ratio = 0; | ||
130 | geo->dst.width = geo->src.width; | ||
131 | } | ||
132 | 157 | ||
133 | if (geo->dst.height >= 2 * geo->src.height) { | 158 | case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ |
134 | geo->y_ratio = 1; | 159 | flags = 0; |
135 | geo->dst.height = 2 * geo->src.height; | 160 | /* fall through */ |
136 | } else { | 161 | |
137 | geo->y_ratio = 0; | 162 | case MXR_GEOMETRY_COMPOSE: |
138 | geo->dst.height = geo->src.height; | 163 | /* remember center of the area */ |
139 | } | 164 | x_center = dst->x_offset + dst->width / 2; |
165 | y_center = dst->y_offset + dst->height / 2; | ||
166 | /* round up/down to 2 multiple depending on flags */ | ||
167 | if (flags & V4L2_SEL_FLAG_LE) { | ||
168 | dst->width = round_down(dst->width, 2); | ||
169 | dst->height = round_down(dst->height, 2); | ||
170 | } else { | ||
171 | dst->width = round_up(dst->width, 2); | ||
172 | dst->height = round_up(dst->height, 2); | ||
173 | } | ||
174 | /* assure that compose rect is inside display area */ | ||
175 | dst->width = min(dst->width, dst->full_width); | ||
176 | dst->height = min(dst->height, dst->full_height); | ||
177 | |||
178 | /* ensure that compose is reachable using 2x scaling */ | ||
179 | dst->width = min(dst->width, 2 * src->full_width); | ||
180 | dst->height = min(dst->height, 2 * src->full_height); | ||
181 | |||
182 | /* setup offsets */ | ||
183 | dst->x_offset = do_center(x_center, dst->width, | ||
184 | dst->full_width, flags); | ||
185 | dst->y_offset = do_center(y_center, dst->height, | ||
186 | dst->full_height, flags); | ||
187 | flags = 0; | ||
188 | /* fall through */ | ||
140 | 189 | ||
141 | geo->dst.x_offset = min(geo->dst.x_offset, | 190 | case MXR_GEOMETRY_CROP: |
142 | geo->dst.full_width - geo->dst.width); | 191 | /* remember center of the area */ |
143 | geo->dst.y_offset = min(geo->dst.y_offset, | 192 | x_center = src->x_offset + src->width / 2; |
144 | geo->dst.full_height - geo->dst.height); | 193 | y_center = src->y_offset + src->height / 2; |
194 | /* ensure that cropping area lies inside the buffer */ | ||
195 | if (src->full_width < dst->width) | ||
196 | src->width = dst->width / 2; | ||
197 | else | ||
198 | src->width = closest(src->width, dst->width / 2, | ||
199 | dst->width, flags); | ||
200 | |||
201 | if (src->width == dst->width) | ||
202 | geo->x_ratio = 0; | ||
203 | else | ||
204 | geo->x_ratio = 1; | ||
205 | |||
206 | if (src->full_height < dst->height) | ||
207 | src->height = dst->height / 2; | ||
208 | else | ||
209 | src->height = closest(src->height, dst->height / 2, | ||
210 | dst->height, flags); | ||
211 | |||
212 | if (src->height == dst->height) | ||
213 | geo->y_ratio = 0; | ||
214 | else | ||
215 | geo->y_ratio = 1; | ||
216 | |||
217 | /* setup offsets */ | ||
218 | src->x_offset = do_center(x_center, src->width, | ||
219 | src->full_width, flags); | ||
220 | src->y_offset = do_center(y_center, src->height, | ||
221 | src->full_height, flags); | ||
222 | flags = 0; | ||
223 | /* fall through */ | ||
224 | case MXR_GEOMETRY_SOURCE: | ||
225 | src->full_width = clamp_val(src->full_width, | ||
226 | src->width + src->x_offset, 32767); | ||
227 | src->full_height = clamp_val(src->full_height, | ||
228 | src->height + src->y_offset, 2047); | ||
229 | }; | ||
145 | } | 230 | } |
146 | 231 | ||
147 | /* PUBLIC API */ | 232 | /* PUBLIC API */ |
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index b47d0c06ecf5..7884baeff76a 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c | |||
@@ -170,18 +170,22 @@ static int mxr_querycap(struct file *file, void *priv, | |||
170 | return 0; | 170 | return 0; |
171 | } | 171 | } |
172 | 172 | ||
173 | /* Geometry handling */ | 173 | static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) |
174 | static void mxr_layer_geo_fix(struct mxr_layer *layer) | ||
175 | { | 174 | { |
176 | struct mxr_device *mdev = layer->mdev; | 175 | mxr_dbg(mdev, "src.full_size = (%u, %u)\n", |
177 | struct v4l2_mbus_framefmt mbus_fmt; | 176 | geo->src.full_width, geo->src.full_height); |
178 | 177 | mxr_dbg(mdev, "src.size = (%u, %u)\n", | |
179 | /* TODO: add some dirty flag to avoid unnecessary adjustments */ | 178 | geo->src.width, geo->src.height); |
180 | mxr_get_mbus_fmt(mdev, &mbus_fmt); | 179 | mxr_dbg(mdev, "src.offset = (%u, %u)\n", |
181 | layer->geo.dst.full_width = mbus_fmt.width; | 180 | geo->src.x_offset, geo->src.y_offset); |
182 | layer->geo.dst.full_height = mbus_fmt.height; | 181 | mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", |
183 | layer->geo.dst.field = mbus_fmt.field; | 182 | geo->dst.full_width, geo->dst.full_height); |
184 | layer->ops.fix_geometry(layer); | 183 | mxr_dbg(mdev, "dst.size = (%u, %u)\n", |
184 | geo->dst.width, geo->dst.height); | ||
185 | mxr_dbg(mdev, "dst.offset = (%u, %u)\n", | ||
186 | geo->dst.x_offset, geo->dst.y_offset); | ||
187 | mxr_dbg(mdev, "ratio = (%u, %u)\n", | ||
188 | geo->x_ratio, geo->y_ratio); | ||
185 | } | 189 | } |
186 | 190 | ||
187 | static void mxr_layer_default_geo(struct mxr_layer *layer) | 191 | static void mxr_layer_default_geo(struct mxr_layer *layer) |
@@ -204,27 +208,29 @@ static void mxr_layer_default_geo(struct mxr_layer *layer) | |||
204 | layer->geo.src.width = layer->geo.src.full_width; | 208 | layer->geo.src.width = layer->geo.src.full_width; |
205 | layer->geo.src.height = layer->geo.src.full_height; | 209 | layer->geo.src.height = layer->geo.src.full_height; |
206 | 210 | ||
207 | layer->ops.fix_geometry(layer); | 211 | mxr_geometry_dump(mdev, &layer->geo); |
212 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); | ||
213 | mxr_geometry_dump(mdev, &layer->geo); | ||
208 | } | 214 | } |
209 | 215 | ||
210 | static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) | 216 | static void mxr_layer_update_output(struct mxr_layer *layer) |
211 | { | 217 | { |
212 | mxr_dbg(mdev, "src.full_size = (%u, %u)\n", | 218 | struct mxr_device *mdev = layer->mdev; |
213 | geo->src.full_width, geo->src.full_height); | 219 | struct v4l2_mbus_framefmt mbus_fmt; |
214 | mxr_dbg(mdev, "src.size = (%u, %u)\n", | 220 | |
215 | geo->src.width, geo->src.height); | 221 | mxr_get_mbus_fmt(mdev, &mbus_fmt); |
216 | mxr_dbg(mdev, "src.offset = (%u, %u)\n", | 222 | /* checking if update is needed */ |
217 | geo->src.x_offset, geo->src.y_offset); | 223 | if (layer->geo.dst.full_width == mbus_fmt.width && |
218 | mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", | 224 | layer->geo.dst.full_height == mbus_fmt.width) |
219 | geo->dst.full_width, geo->dst.full_height); | 225 | return; |
220 | mxr_dbg(mdev, "dst.size = (%u, %u)\n", | ||
221 | geo->dst.width, geo->dst.height); | ||
222 | mxr_dbg(mdev, "dst.offset = (%u, %u)\n", | ||
223 | geo->dst.x_offset, geo->dst.y_offset); | ||
224 | mxr_dbg(mdev, "ratio = (%u, %u)\n", | ||
225 | geo->x_ratio, geo->y_ratio); | ||
226 | } | ||
227 | 226 | ||
227 | layer->geo.dst.full_width = mbus_fmt.width; | ||
228 | layer->geo.dst.full_height = mbus_fmt.height; | ||
229 | layer->geo.dst.field = mbus_fmt.field; | ||
230 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); | ||
231 | |||
232 | mxr_geometry_dump(mdev, &layer->geo); | ||
233 | } | ||
228 | 234 | ||
229 | static const struct mxr_format *find_format_by_fourcc( | 235 | static const struct mxr_format *find_format_by_fourcc( |
230 | struct mxr_layer *layer, unsigned long fourcc); | 236 | struct mxr_layer *layer, unsigned long fourcc); |
@@ -249,37 +255,6 @@ static int mxr_enum_fmt(struct file *file, void *priv, | |||
249 | return 0; | 255 | return 0; |
250 | } | 256 | } |
251 | 257 | ||
252 | static int mxr_s_fmt(struct file *file, void *priv, | ||
253 | struct v4l2_format *f) | ||
254 | { | ||
255 | struct mxr_layer *layer = video_drvdata(file); | ||
256 | const struct mxr_format *fmt; | ||
257 | struct v4l2_pix_format_mplane *pix; | ||
258 | struct mxr_device *mdev = layer->mdev; | ||
259 | struct mxr_geometry *geo = &layer->geo; | ||
260 | |||
261 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); | ||
262 | |||
263 | pix = &f->fmt.pix_mp; | ||
264 | fmt = find_format_by_fourcc(layer, pix->pixelformat); | ||
265 | if (fmt == NULL) { | ||
266 | mxr_warn(mdev, "not recognized fourcc: %08x\n", | ||
267 | pix->pixelformat); | ||
268 | return -EINVAL; | ||
269 | } | ||
270 | layer->fmt = fmt; | ||
271 | geo->src.full_width = pix->width; | ||
272 | geo->src.width = pix->width; | ||
273 | geo->src.full_height = pix->height; | ||
274 | geo->src.height = pix->height; | ||
275 | /* assure consistency of geometry */ | ||
276 | mxr_layer_geo_fix(layer); | ||
277 | mxr_dbg(mdev, "width=%u height=%u span=%u\n", | ||
278 | geo->src.width, geo->src.height, geo->src.full_width); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static unsigned int divup(unsigned int divident, unsigned int divisor) | 258 | static unsigned int divup(unsigned int divident, unsigned int divisor) |
284 | { | 259 | { |
285 | return (divident + divisor - 1) / divisor; | 260 | return (divident + divisor - 1) / divisor; |
@@ -299,6 +274,10 @@ static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, | |||
299 | { | 274 | { |
300 | int i; | 275 | int i; |
301 | 276 | ||
277 | /* checking if nothing to fill */ | ||
278 | if (!planes) | ||
279 | return; | ||
280 | |||
302 | memset(planes, 0, sizeof(*planes) * fmt->num_subframes); | 281 | memset(planes, 0, sizeof(*planes) * fmt->num_subframes); |
303 | for (i = 0; i < fmt->num_planes; ++i) { | 282 | for (i = 0; i < fmt->num_planes; ++i) { |
304 | struct v4l2_plane_pix_format *plane = planes | 283 | struct v4l2_plane_pix_format *plane = planes |
@@ -332,73 +311,194 @@ static int mxr_g_fmt(struct file *file, void *priv, | |||
332 | return 0; | 311 | return 0; |
333 | } | 312 | } |
334 | 313 | ||
335 | static inline struct mxr_crop *choose_crop_by_type(struct mxr_geometry *geo, | 314 | static int mxr_s_fmt(struct file *file, void *priv, |
336 | enum v4l2_buf_type type) | 315 | struct v4l2_format *f) |
337 | { | ||
338 | switch (type) { | ||
339 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: | ||
340 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | ||
341 | return &geo->dst; | ||
342 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | ||
343 | return &geo->src; | ||
344 | default: | ||
345 | return NULL; | ||
346 | } | ||
347 | } | ||
348 | |||
349 | static int mxr_g_crop(struct file *file, void *fh, struct v4l2_crop *a) | ||
350 | { | 316 | { |
351 | struct mxr_layer *layer = video_drvdata(file); | 317 | struct mxr_layer *layer = video_drvdata(file); |
352 | struct mxr_crop *crop; | 318 | const struct mxr_format *fmt; |
319 | struct v4l2_pix_format_mplane *pix; | ||
320 | struct mxr_device *mdev = layer->mdev; | ||
321 | struct mxr_geometry *geo = &layer->geo; | ||
353 | 322 | ||
354 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | 323 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); |
355 | crop = choose_crop_by_type(&layer->geo, a->type); | 324 | |
356 | if (crop == NULL) | 325 | pix = &f->fmt.pix_mp; |
326 | fmt = find_format_by_fourcc(layer, pix->pixelformat); | ||
327 | if (fmt == NULL) { | ||
328 | mxr_warn(mdev, "not recognized fourcc: %08x\n", | ||
329 | pix->pixelformat); | ||
357 | return -EINVAL; | 330 | return -EINVAL; |
358 | mxr_layer_geo_fix(layer); | 331 | } |
359 | a->c.left = crop->x_offset; | 332 | layer->fmt = fmt; |
360 | a->c.top = crop->y_offset; | 333 | /* set source size to highest accepted value */ |
361 | a->c.width = crop->width; | 334 | geo->src.full_width = max(geo->dst.full_width, pix->width); |
362 | a->c.height = crop->height; | 335 | geo->src.full_height = max(geo->dst.full_height, pix->height); |
336 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); | ||
337 | mxr_geometry_dump(mdev, &layer->geo); | ||
338 | /* set cropping to total visible screen */ | ||
339 | geo->src.width = pix->width; | ||
340 | geo->src.height = pix->height; | ||
341 | geo->src.x_offset = 0; | ||
342 | geo->src.y_offset = 0; | ||
343 | /* assure consistency of geometry */ | ||
344 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); | ||
345 | mxr_geometry_dump(mdev, &layer->geo); | ||
346 | /* set full size to lowest possible value */ | ||
347 | geo->src.full_width = 0; | ||
348 | geo->src.full_height = 0; | ||
349 | layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); | ||
350 | mxr_geometry_dump(mdev, &layer->geo); | ||
351 | |||
352 | /* returning results */ | ||
353 | mxr_g_fmt(file, priv, f); | ||
354 | |||
363 | return 0; | 355 | return 0; |
364 | } | 356 | } |
365 | 357 | ||
366 | static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a) | 358 | static int mxr_g_selection(struct file *file, void *fh, |
359 | struct v4l2_selection *s) | ||
367 | { | 360 | { |
368 | struct mxr_layer *layer = video_drvdata(file); | 361 | struct mxr_layer *layer = video_drvdata(file); |
369 | struct mxr_crop *crop; | 362 | struct mxr_geometry *geo = &layer->geo; |
370 | 363 | ||
371 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | 364 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); |
372 | crop = choose_crop_by_type(&layer->geo, a->type); | 365 | |
373 | if (crop == NULL) | 366 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && |
367 | s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
368 | return -EINVAL; | ||
369 | |||
370 | switch (s->target) { | ||
371 | case V4L2_SEL_TGT_CROP_ACTIVE: | ||
372 | s->r.left = geo->src.x_offset; | ||
373 | s->r.top = geo->src.y_offset; | ||
374 | s->r.width = geo->src.width; | ||
375 | s->r.height = geo->src.height; | ||
376 | break; | ||
377 | case V4L2_SEL_TGT_CROP_DEFAULT: | ||
378 | case V4L2_SEL_TGT_CROP_BOUNDS: | ||
379 | s->r.left = 0; | ||
380 | s->r.top = 0; | ||
381 | s->r.width = geo->src.full_width; | ||
382 | s->r.height = geo->src.full_height; | ||
383 | break; | ||
384 | case V4L2_SEL_TGT_COMPOSE_ACTIVE: | ||
385 | case V4L2_SEL_TGT_COMPOSE_PADDED: | ||
386 | s->r.left = geo->dst.x_offset; | ||
387 | s->r.top = geo->dst.y_offset; | ||
388 | s->r.width = geo->dst.width; | ||
389 | s->r.height = geo->dst.height; | ||
390 | break; | ||
391 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | ||
392 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
393 | s->r.left = 0; | ||
394 | s->r.top = 0; | ||
395 | s->r.width = geo->dst.full_width; | ||
396 | s->r.height = geo->dst.full_height; | ||
397 | break; | ||
398 | default: | ||
374 | return -EINVAL; | 399 | return -EINVAL; |
375 | crop->x_offset = a->c.left; | 400 | } |
376 | crop->y_offset = a->c.top; | 401 | |
377 | crop->width = a->c.width; | ||
378 | crop->height = a->c.height; | ||
379 | mxr_layer_geo_fix(layer); | ||
380 | return 0; | 402 | return 0; |
381 | } | 403 | } |
382 | 404 | ||
383 | static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) | 405 | /* returns 1 if rectangle 'a' is inside 'b' */ |
406 | static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b) | ||
407 | { | ||
408 | if (a->left < b->left) | ||
409 | return 0; | ||
410 | if (a->top < b->top) | ||
411 | return 0; | ||
412 | if (a->left + a->width > b->left + b->width) | ||
413 | return 0; | ||
414 | if (a->top + a->height > b->top + b->height) | ||
415 | return 0; | ||
416 | return 1; | ||
417 | } | ||
418 | |||
419 | static int mxr_s_selection(struct file *file, void *fh, | ||
420 | struct v4l2_selection *s) | ||
384 | { | 421 | { |
385 | struct mxr_layer *layer = video_drvdata(file); | 422 | struct mxr_layer *layer = video_drvdata(file); |
386 | struct mxr_crop *crop; | 423 | struct mxr_geometry *geo = &layer->geo; |
424 | struct mxr_crop *target = NULL; | ||
425 | enum mxr_geometry_stage stage; | ||
426 | struct mxr_geometry tmp; | ||
427 | struct v4l2_rect res; | ||
387 | 428 | ||
388 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | 429 | memset(&res, 0, sizeof res); |
389 | crop = choose_crop_by_type(&layer->geo, a->type); | 430 | |
390 | if (crop == NULL) | 431 | mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, |
432 | s->r.width, s->r.height, s->r.left, s->r.top); | ||
433 | |||
434 | if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && | ||
435 | s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
391 | return -EINVAL; | 436 | return -EINVAL; |
392 | mxr_layer_geo_fix(layer); | 437 | |
393 | a->bounds.left = 0; | 438 | switch (s->target) { |
394 | a->bounds.top = 0; | 439 | /* ignore read-only targets */ |
395 | a->bounds.width = crop->full_width; | 440 | case V4L2_SEL_TGT_CROP_DEFAULT: |
396 | a->bounds.top = crop->full_height; | 441 | case V4L2_SEL_TGT_CROP_BOUNDS: |
397 | a->defrect = a->bounds; | 442 | res.width = geo->src.full_width; |
398 | /* setting pixel aspect to 1/1 */ | 443 | res.height = geo->src.full_height; |
399 | a->pixelaspect.numerator = 1; | 444 | break; |
400 | a->pixelaspect.denominator = 1; | 445 | |
446 | /* ignore read-only targets */ | ||
447 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | ||
448 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | ||
449 | res.width = geo->dst.full_width; | ||
450 | res.height = geo->dst.full_height; | ||
451 | break; | ||
452 | |||
453 | case V4L2_SEL_TGT_CROP_ACTIVE: | ||
454 | target = &geo->src; | ||
455 | stage = MXR_GEOMETRY_CROP; | ||
456 | break; | ||
457 | case V4L2_SEL_TGT_COMPOSE_ACTIVE: | ||
458 | case V4L2_SEL_TGT_COMPOSE_PADDED: | ||
459 | target = &geo->dst; | ||
460 | stage = MXR_GEOMETRY_COMPOSE; | ||
461 | break; | ||
462 | default: | ||
463 | return -EINVAL; | ||
464 | } | ||
465 | /* apply change and update geometry if needed */ | ||
466 | if (target) { | ||
467 | /* backup current geometry if setup fails */ | ||
468 | memcpy(&tmp, geo, sizeof tmp); | ||
469 | |||
470 | /* apply requested selection */ | ||
471 | target->x_offset = s->r.left; | ||
472 | target->y_offset = s->r.top; | ||
473 | target->width = s->r.width; | ||
474 | target->height = s->r.height; | ||
475 | |||
476 | layer->ops.fix_geometry(layer, stage, s->flags); | ||
477 | |||
478 | /* retrieve update selection rectangle */ | ||
479 | res.left = target->x_offset; | ||
480 | res.top = target->y_offset; | ||
481 | res.width = target->width; | ||
482 | res.height = target->height; | ||
483 | |||
484 | mxr_geometry_dump(layer->mdev, &layer->geo); | ||
485 | } | ||
486 | |||
487 | /* checking if the rectangle satisfies constraints */ | ||
488 | if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r)) | ||
489 | goto fail; | ||
490 | if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res)) | ||
491 | goto fail; | ||
492 | |||
493 | /* return result rectangle */ | ||
494 | s->r = res; | ||
495 | |||
401 | return 0; | 496 | return 0; |
497 | fail: | ||
498 | /* restore old geometry, which is not touched if target is NULL */ | ||
499 | if (target) | ||
500 | memcpy(geo, &tmp, sizeof tmp); | ||
501 | return -ERANGE; | ||
402 | } | 502 | } |
403 | 503 | ||
404 | static int mxr_enum_dv_presets(struct file *file, void *fh, | 504 | static int mxr_enum_dv_presets(struct file *file, void *fh, |
@@ -438,6 +538,8 @@ static int mxr_s_dv_preset(struct file *file, void *fh, | |||
438 | 538 | ||
439 | mutex_unlock(&mdev->mutex); | 539 | mutex_unlock(&mdev->mutex); |
440 | 540 | ||
541 | mxr_layer_update_output(layer); | ||
542 | |||
441 | /* any failure should return EINVAL according to V4L2 doc */ | 543 | /* any failure should return EINVAL according to V4L2 doc */ |
442 | return ret ? -EINVAL : 0; | 544 | return ret ? -EINVAL : 0; |
443 | } | 545 | } |
@@ -478,6 +580,8 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) | |||
478 | 580 | ||
479 | mutex_unlock(&mdev->mutex); | 581 | mutex_unlock(&mdev->mutex); |
480 | 582 | ||
583 | mxr_layer_update_output(layer); | ||
584 | |||
481 | return ret ? -EINVAL : 0; | 585 | return ret ? -EINVAL : 0; |
482 | } | 586 | } |
483 | 587 | ||
@@ -526,25 +630,27 @@ static int mxr_s_output(struct file *file, void *fh, unsigned int i) | |||
526 | struct video_device *vfd = video_devdata(file); | 630 | struct video_device *vfd = video_devdata(file); |
527 | struct mxr_layer *layer = video_drvdata(file); | 631 | struct mxr_layer *layer = video_drvdata(file); |
528 | struct mxr_device *mdev = layer->mdev; | 632 | struct mxr_device *mdev = layer->mdev; |
529 | int ret = 0; | ||
530 | 633 | ||
531 | if (i >= mdev->output_cnt || mdev->output[i] == NULL) | 634 | if (i >= mdev->output_cnt || mdev->output[i] == NULL) |
532 | return -EINVAL; | 635 | return -EINVAL; |
533 | 636 | ||
534 | mutex_lock(&mdev->mutex); | 637 | mutex_lock(&mdev->mutex); |
535 | if (mdev->n_output > 0) { | 638 | if (mdev->n_output > 0) { |
536 | ret = -EBUSY; | 639 | mutex_unlock(&mdev->mutex); |
537 | goto done; | 640 | return -EBUSY; |
538 | } | 641 | } |
539 | mdev->current_output = i; | 642 | mdev->current_output = i; |
540 | vfd->tvnorms = 0; | 643 | vfd->tvnorms = 0; |
541 | v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, | 644 | v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, |
542 | &vfd->tvnorms); | 645 | &vfd->tvnorms); |
646 | mutex_unlock(&mdev->mutex); | ||
647 | |||
648 | /* update layers geometry */ | ||
649 | mxr_layer_update_output(layer); | ||
650 | |||
543 | mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); | 651 | mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); |
544 | 652 | ||
545 | done: | 653 | return 0; |
546 | mutex_unlock(&mdev->mutex); | ||
547 | return ret; | ||
548 | } | 654 | } |
549 | 655 | ||
550 | static int mxr_g_output(struct file *file, void *fh, unsigned int *p) | 656 | static int mxr_g_output(struct file *file, void *fh, unsigned int *p) |
@@ -633,10 +739,9 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = { | |||
633 | .vidioc_enum_output = mxr_enum_output, | 739 | .vidioc_enum_output = mxr_enum_output, |
634 | .vidioc_s_output = mxr_s_output, | 740 | .vidioc_s_output = mxr_s_output, |
635 | .vidioc_g_output = mxr_g_output, | 741 | .vidioc_g_output = mxr_g_output, |
636 | /* Crop ioctls */ | 742 | /* selection ioctls */ |
637 | .vidioc_g_crop = mxr_g_crop, | 743 | .vidioc_g_selection = mxr_g_selection, |
638 | .vidioc_s_crop = mxr_s_crop, | 744 | .vidioc_s_selection = mxr_s_selection, |
639 | .vidioc_cropcap = mxr_cropcap, | ||
640 | }; | 745 | }; |
641 | 746 | ||
642 | static int mxr_video_open(struct file *file) | 747 | static int mxr_video_open(struct file *file) |
@@ -805,10 +910,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) | |||
805 | /* block any changes in output configuration */ | 910 | /* block any changes in output configuration */ |
806 | mxr_output_get(mdev); | 911 | mxr_output_get(mdev); |
807 | 912 | ||
808 | /* update layers geometry */ | 913 | mxr_layer_update_output(layer); |
809 | mxr_layer_geo_fix(layer); | ||
810 | mxr_geometry_dump(mdev, &layer->geo); | ||
811 | |||
812 | layer->ops.format_set(layer); | 914 | layer->ops.format_set(layer); |
813 | /* enabling layer in hardware */ | 915 | /* enabling layer in hardware */ |
814 | spin_lock_irqsave(&layer->enq_slock, flags); | 916 | spin_lock_irqsave(&layer->enq_slock, flags); |
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c index f3bb2e34cb51..e41ec2ec45f8 100644 --- a/drivers/media/video/s5p-tv/mixer_vp_layer.c +++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c | |||
@@ -127,47 +127,77 @@ static void mxr_vp_format_set(struct mxr_layer *layer) | |||
127 | mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo); | 127 | mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo); |
128 | } | 128 | } |
129 | 129 | ||
130 | static void mxr_vp_fix_geometry(struct mxr_layer *layer) | 130 | static inline unsigned int do_center(unsigned int center, |
131 | unsigned int size, unsigned int upper, unsigned int flags) | ||
131 | { | 132 | { |
132 | struct mxr_geometry *geo = &layer->geo; | 133 | unsigned int lower; |
134 | |||
135 | if (flags & MXR_NO_OFFSET) | ||
136 | return 0; | ||
137 | |||
138 | lower = center - min(center, size / 2); | ||
139 | return min(lower, upper - size); | ||
140 | } | ||
133 | 141 | ||
134 | /* align horizontal size to 8 pixels */ | 142 | static void mxr_vp_fix_geometry(struct mxr_layer *layer, |
135 | geo->src.full_width = ALIGN(geo->src.full_width, 8); | 143 | enum mxr_geometry_stage stage, unsigned long flags) |
136 | /* limit to boundary size */ | 144 | { |
137 | geo->src.full_width = clamp_val(geo->src.full_width, 8, 8192); | 145 | struct mxr_geometry *geo = &layer->geo; |
138 | geo->src.full_height = clamp_val(geo->src.full_height, 1, 8192); | 146 | struct mxr_crop *src = &geo->src; |
139 | geo->src.width = clamp_val(geo->src.width, 32, geo->src.full_width); | 147 | struct mxr_crop *dst = &geo->dst; |
140 | geo->src.width = min(geo->src.width, 2047U); | 148 | unsigned long x_center, y_center; |
141 | geo->src.height = clamp_val(geo->src.height, 4, geo->src.full_height); | 149 | |
142 | geo->src.height = min(geo->src.height, 2047U); | 150 | switch (stage) { |
143 | 151 | ||
144 | /* setting size of output window */ | 152 | case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ |
145 | geo->dst.width = clamp_val(geo->dst.width, 8, geo->dst.full_width); | 153 | case MXR_GEOMETRY_COMPOSE: |
146 | geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height); | 154 | /* remember center of the area */ |
147 | 155 | x_center = dst->x_offset + dst->width / 2; | |
148 | /* ensure that scaling is in range 1/4x to 16x */ | 156 | y_center = dst->y_offset + dst->height / 2; |
149 | if (geo->src.width >= 4 * geo->dst.width) | 157 | |
150 | geo->src.width = 4 * geo->dst.width; | 158 | /* ensure that compose is reachable using 16x scaling */ |
151 | if (geo->dst.width >= 16 * geo->src.width) | 159 | dst->width = clamp(dst->width, 8U, 16 * src->full_width); |
152 | geo->dst.width = 16 * geo->src.width; | 160 | dst->height = clamp(dst->height, 1U, 16 * src->full_height); |
153 | if (geo->src.height >= 4 * geo->dst.height) | 161 | |
154 | geo->src.height = 4 * geo->dst.height; | 162 | /* setup offsets */ |
155 | if (geo->dst.height >= 16 * geo->src.height) | 163 | dst->x_offset = do_center(x_center, dst->width, |
156 | geo->dst.height = 16 * geo->src.height; | 164 | dst->full_width, flags); |
157 | 165 | dst->y_offset = do_center(y_center, dst->height, | |
158 | /* setting scaling ratio */ | 166 | dst->full_height, flags); |
159 | geo->x_ratio = (geo->src.width << 16) / geo->dst.width; | 167 | flags = 0; /* remove possible MXR_NO_OFFSET flag */ |
160 | geo->y_ratio = (geo->src.height << 16) / geo->dst.height; | 168 | /* fall through */ |
161 | 169 | case MXR_GEOMETRY_CROP: | |
162 | /* adjust offsets */ | 170 | /* remember center of the area */ |
163 | geo->src.x_offset = min(geo->src.x_offset, | 171 | x_center = src->x_offset + src->width / 2; |
164 | geo->src.full_width - geo->src.width); | 172 | y_center = src->y_offset + src->height / 2; |
165 | geo->src.y_offset = min(geo->src.y_offset, | 173 | |
166 | geo->src.full_height - geo->src.height); | 174 | /* ensure scaling is between 0.25x .. 16x */ |
167 | geo->dst.x_offset = min(geo->dst.x_offset, | 175 | src->width = clamp(src->width, round_up(dst->width, 4), |
168 | geo->dst.full_width - geo->dst.width); | 176 | dst->width * 16); |
169 | geo->dst.y_offset = min(geo->dst.y_offset, | 177 | src->height = clamp(src->height, round_up(dst->height, 4), |
170 | geo->dst.full_height - geo->dst.height); | 178 | dst->height * 16); |
179 | |||
180 | /* hardware limits */ | ||
181 | src->width = clamp(src->width, 32U, 2047U); | ||
182 | src->height = clamp(src->height, 4U, 2047U); | ||
183 | |||
184 | /* setup offsets */ | ||
185 | src->x_offset = do_center(x_center, src->width, | ||
186 | src->full_width, flags); | ||
187 | src->y_offset = do_center(y_center, src->height, | ||
188 | src->full_height, flags); | ||
189 | |||
190 | /* setting scaling ratio */ | ||
191 | geo->x_ratio = (src->width << 16) / dst->width; | ||
192 | geo->y_ratio = (src->height << 16) / dst->height; | ||
193 | /* fall through */ | ||
194 | |||
195 | case MXR_GEOMETRY_SOURCE: | ||
196 | src->full_width = clamp(src->full_width, | ||
197 | ALIGN(src->width + src->x_offset, 8), 8192U); | ||
198 | src->full_height = clamp(src->full_height, | ||
199 | src->height + src->y_offset, 8192U); | ||
200 | }; | ||
171 | } | 201 | } |
172 | 202 | ||
173 | /* PUBLIC API */ | 203 | /* PUBLIC API */ |