aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorTomasz Stanislawski <t.stanislaws@samsung.com>2011-08-25 06:14:26 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-01-10 20:19:23 -0500
commit0d066d3f6fdfd189bef5fe653477f5f9db2eb225 (patch)
tree34b4e8b099292ea346692141f330199f17a181d9 /drivers/media
parent992efeff79fe8de44d4e8b7636bb2e5d9dcf698d (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/media')
-rw-r--r--drivers/media/video/s5p-tv/mixer.h14
-rw-r--r--drivers/media/video/s5p-tv/mixer_grp_layer.c157
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c342
-rw-r--r--drivers/media/video/s5p-tv/mixer_vp_layer.c108
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 51ad59b3035..1597078c4a5 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 */
90enum 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 */
90struct mxr_geometry { 101struct 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 de8270c2b6e..b93a21f5aa1 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
104static void mxr_graph_fix_geometry(struct mxr_layer *layer) 104static 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
136static 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
148static 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 b47d0c06ecf..7884baeff76 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 */ 173static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
174static 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
187static void mxr_layer_default_geo(struct mxr_layer *layer) 191static 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
210static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) 216static 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
229static const struct mxr_format *find_format_by_fourcc( 235static 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
252static 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
283static unsigned int divup(unsigned int divident, unsigned int divisor) 258static 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
335static inline struct mxr_crop *choose_crop_by_type(struct mxr_geometry *geo, 314static 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
349static 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
366static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a) 358static 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
383static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) 405/* returns 1 if rectangle 'a' is inside 'b' */
406static 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
419static 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;
497fail:
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
404static int mxr_enum_dv_presets(struct file *file, void *fh, 504static 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
545done: 653 return 0;
546 mutex_unlock(&mdev->mutex);
547 return ret;
548} 654}
549 655
550static int mxr_g_output(struct file *file, void *fh, unsigned int *p) 656static 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
642static int mxr_video_open(struct file *file) 747static 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 f3bb2e34cb5..e41ec2ec45f 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
130static void mxr_vp_fix_geometry(struct mxr_layer *layer) 130static 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 */ 142static 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 */