Commit | Line | Data |
---|---|---|
fef1c8d0 TS |
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) | |
2d700715 | 89 | addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); |
fef1c8d0 TS |
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 | ||
0d066d3f TS |
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) | |
fef1c8d0 TS |
150 | { |
151 | struct mxr_geometry *geo = &layer->geo; | |
0d066d3f TS |
152 | struct mxr_crop *src = &geo->src; |
153 | struct mxr_crop *dst = &geo->dst; | |
154 | unsigned int x_center, y_center; | |
fef1c8d0 | 155 | |
0d066d3f | 156 | switch (stage) { |
fef1c8d0 | 157 | |
0d066d3f TS |
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 */ | |
fef1c8d0 | 189 | |
0d066d3f TS |
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); | |
2028c71d | 229 | } |
fef1c8d0 TS |
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; | |
adbedc29 | 238 | const struct mxr_layer_ops ops = { |
fef1c8d0 TS |
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 |