74344c764daad5ebe4fa19011a83d70f5f8751e1
[sfrench/cifs-2.6.git] / drivers / media / platform / s5p-tv / mixer_grp_layer.c
1 /*
2  * Samsung TV Mixer driver
3  *
4  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
5  *
6  * Tomasz Stanislawski, <t.stanislaws@samsung.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
10  * by the Free Software Foundiation. either version 2 of the License,
11  * or (at your option) any later version
12  */
13
14 #include "mixer.h"
15
16 #include <media/videobuf2-dma-contig.h>
17
18 /* FORMAT DEFINITIONS */
19
20 static const struct mxr_format mxr_fb_fmt_rgb565 = {
21         .name = "RGB565",
22         .fourcc = V4L2_PIX_FMT_RGB565,
23         .colorspace = V4L2_COLORSPACE_SRGB,
24         .num_planes = 1,
25         .plane = {
26                 { .width = 1, .height = 1, .size = 2 },
27         },
28         .num_subframes = 1,
29         .cookie = 4,
30 };
31
32 static const struct mxr_format mxr_fb_fmt_argb1555 = {
33         .name = "ARGB1555",
34         .num_planes = 1,
35         .fourcc = V4L2_PIX_FMT_RGB555,
36         .colorspace = V4L2_COLORSPACE_SRGB,
37         .plane = {
38                 { .width = 1, .height = 1, .size = 2 },
39         },
40         .num_subframes = 1,
41         .cookie = 5,
42 };
43
44 static const struct mxr_format mxr_fb_fmt_argb4444 = {
45         .name = "ARGB4444",
46         .num_planes = 1,
47         .fourcc = V4L2_PIX_FMT_RGB444,
48         .colorspace = V4L2_COLORSPACE_SRGB,
49         .plane = {
50                 { .width = 1, .height = 1, .size = 2 },
51         },
52         .num_subframes = 1,
53         .cookie = 6,
54 };
55
56 static const struct mxr_format mxr_fb_fmt_argb8888 = {
57         .name = "ARGB8888",
58         .fourcc = V4L2_PIX_FMT_BGR32,
59         .colorspace = V4L2_COLORSPACE_SRGB,
60         .num_planes = 1,
61         .plane = {
62                 { .width = 1, .height = 1, .size = 4 },
63         },
64         .num_subframes = 1,
65         .cookie = 7,
66 };
67
68 static const struct mxr_format *mxr_graph_format[] = {
69         &mxr_fb_fmt_rgb565,
70         &mxr_fb_fmt_argb1555,
71         &mxr_fb_fmt_argb4444,
72         &mxr_fb_fmt_argb8888,
73 };
74
75 /* AUXILIARY CALLBACKS */
76
77 static void mxr_graph_layer_release(struct mxr_layer *layer)
78 {
79         mxr_base_layer_unregister(layer);
80         mxr_base_layer_release(layer);
81 }
82
83 static void mxr_graph_buffer_set(struct mxr_layer *layer,
84         struct mxr_buffer *buf)
85 {
86         dma_addr_t addr = 0;
87
88         if (buf)
89                 addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
90         mxr_reg_graph_buffer(layer->mdev, layer->idx, addr);
91 }
92
93 static void mxr_graph_stream_set(struct mxr_layer *layer, int en)
94 {
95         mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en);
96 }
97
98 static void mxr_graph_format_set(struct mxr_layer *layer)
99 {
100         mxr_reg_graph_format(layer->mdev, layer->idx,
101                 layer->fmt, &layer->geo);
102 }
103
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)
150 {
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;
155
156         switch (stage) {
157
158         case MXR_GEOMETRY_SINK: /* nothing to be fixed here */
159                 flags = 0;
160                 /* fall through */
161
162         case MXR_GEOMETRY_COMPOSE:
163                 /* remember center of the area */
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 */
189
190         case MXR_GEOMETRY_CROP:
191                 /* remember center of the area */
192                 x_center = src->x_offset + src->width / 2;
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         }
230 }
231
232 /* PUBLIC API */
233
234 struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx)
235 {
236         struct mxr_layer *layer;
237         int ret;
238         struct mxr_layer_ops ops = {
239                 .release = mxr_graph_layer_release,
240                 .buffer_set = mxr_graph_buffer_set,
241                 .stream_set = mxr_graph_stream_set,
242                 .format_set = mxr_graph_format_set,
243                 .fix_geometry = mxr_graph_fix_geometry,
244         };
245         char name[32];
246
247         sprintf(name, "graph%d", idx);
248
249         layer = mxr_base_layer_create(mdev, idx, name, &ops);
250         if (layer == NULL) {
251                 mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
252                 goto fail;
253         }
254
255         layer->fmt_array = mxr_graph_format;
256         layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format);
257
258         ret = mxr_base_layer_register(layer);
259         if (ret)
260                 goto fail_layer;
261
262         return layer;
263
264 fail_layer:
265         mxr_base_layer_release(layer);
266
267 fail:
268         return NULL;
269 }
270