ENGR00123294 MX53: 4 stripes algorithm for support resizing for big screen
[efikamx:linux-kernel.git] / drivers / mxc / ipu3 / ipu_calc_stripes_sizes.c
1 /*
2  * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
3  */
4
5 /*
6  * The code contained herein is licensed under the GNU General Public
7  * License. You may obtain a copy of the GNU General Public License
8  * Version 2 or later at the following locations:
9  *
10  * http://www.opensource.org/licenses/gpl-license.html
11  * http://www.gnu.org/copyleft/gpl.html
12  */
13
14 /*
15  * @file ipu_calc_stripes_sizes.c
16  *
17  * @brief IPU IC functions
18  *
19  * @ingroup IPU
20  */
21
22 #include <linux/module.h>
23 #include <linux/ipu.h>
24 #include <asm/div64.h>
25
26 #define BPP_32 0
27 #define BPP_16 3
28 #define BPP_8 5
29 #define BPP_24 1
30 #define BPP_12 4
31 #define BPP_18 2
32
33 static u64 _do_div(u64 a, u32 b)
34 {
35         u64 div;
36         div = a;
37         do_div(div, b);
38         return div;
39 }
40
41 static u32 truncate(u32 up, /* 0: down; else: up */
42                                         u64 a, /* must be non-negative */
43                                         u32 b)
44 {
45         u32 d;
46         u64 div;
47         div = _do_div(a, b);
48         d = b * (div >> 32);
49         if (up && (a > (((u64)d) << 32)))
50                 return d+b;
51         else
52                 return d;
53 }
54
55 static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
56 {/* return input_f */
57         unsigned int f_calculated = 0;
58         switch (pfs) {
59         case IPU_PIX_FMT_YVU422P:
60         case IPU_PIX_FMT_YUV422P:
61         case IPU_PIX_FMT_YUV420P2:
62         case IPU_PIX_FMT_YUV420P:
63                 f_calculated = 16;
64                 break;
65
66         case IPU_PIX_FMT_NV12:
67                 f_calculated = 8;
68                 break;
69
70         default:
71                 f_calculated = 0;
72                 break;
73
74         }
75         if (!f_calculated) {
76                 switch (bpp) {
77                 case BPP_32:
78                         f_calculated = 2;
79                         break;
80
81                 case BPP_16:
82                         f_calculated = 4;
83                         break;
84
85                 case BPP_8:
86                 case BPP_24:
87                         f_calculated = 8;
88                         break;
89
90                 case BPP_12:
91                         f_calculated = 16;
92                         break;
93
94                 case BPP_18:
95                         f_calculated = 32;
96                         break;
97
98                 default:
99                         f_calculated = 0;
100                         break;
101                         }
102                 }
103         return f_calculated;
104 }
105
106
107 static unsigned int m_calc(unsigned int pfs)
108 {
109         unsigned int m_calculated = 0;
110         switch (pfs) {
111         case IPU_PIX_FMT_YUV420P2:
112         case IPU_PIX_FMT_YUV420P:
113         case IPU_PIX_FMT_YVU422P:
114         case IPU_PIX_FMT_YUV422P:
115         case IPU_PIX_FMT_YVU420P:
116         case IPU_PIX_FMT_NV12:
117                 m_calculated = 8;
118                 break;
119
120         case IPU_PIX_FMT_YUYV:
121         case IPU_PIX_FMT_UYVY:
122                 m_calculated = 2;
123                 break;
124
125         default:
126                 m_calculated = 1;
127                 break;
128
129         }
130         return m_calculated;
131 }
132
133
134 /* Stripe parameters calculator */
135 /**************************************************************************
136 Notes:
137 MSW = the maximal width allowed for a stripe
138         i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
139 cirr = the maximal inverse resizing ratio for which overlap in the input
140         is requested; typically cirr~2
141 equal_stripes:
142         0: each stripe is allowed to have independent parameters
143                 for maximal image quality
144         1: the stripes are requested to have identical parameters
145         (except the base address), for maximal performance
146 If performance is the top priority (above image quality)
147         Avoid overlap, by setting CIRR = 0
148                 This will also force effectively identical_stripes = 1
149         Choose IF & OF that corresponds to the same IOX/SX for both stripes
150         Choose IFW & OFW such that
151         IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
152         The function returns an error status:
153         0: no error
154         1: invalid input parameters -> aborted without result
155                 Valid parameters should satisfy the following conditions
156                 IFW <= OFW, otherwise downsizing is required
157                                          - which is not supported yet
158                 4 <= IFW,OFW, so some interpolation may be needed even without overlap
159                 IM, OM, IF, OF should not vanish
160                 2*IF <= IFW
161                 so the frame can be split to two equal stripes, even without overlap
162                 2*(OF+IF/irr_opt) <= OFW
163                 so a valid positive INW exists even for equal stripes
164                 OF <= MSW, otherwise, the left stripe cannot be sufficiently large
165                 MSW < OFW, so splitting to stripes is required
166                 OFW <= 2*MSW, so two stripes are sufficient
167                 (this also implies that 2<=MSW)
168         2: OF is not a multiple of OM - not fully-supported yet
169         Output is produced but OW is not guaranited to be a multiple of OM
170         4: OFW reduced to be a multiple of OM
171         8: CIRR > 1: truncated to 1
172         Overlap is not supported (and not needed) y for upsizing)
173 **************************************************************************/
174 int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
175                            /* input frame width;>1 */
176                            unsigned int output_frame_width, /* output frame width; >1 */
177                            const unsigned int maximal_stripe_width,
178                            /* the maximal width allowed for a stripe */
179                            const unsigned long long cirr, /* see above */
180                            const unsigned int equal_stripes, /* see above */
181                            u32 input_pixelformat,/* pixel format after of read channel*/
182                            u32 output_pixelformat,/* pixel format after of write channel*/
183                            struct stripe_param *left,
184                            struct stripe_param *right)
185 {
186         const unsigned int irr_frac_bits = 13;
187         const unsigned long irr_steps = 1 << irr_frac_bits;
188         const u64 dirr = ((u64)1) << (32 - 2);
189         /* The maximum relative difference allowed between the irrs */
190         const u64 cr = ((u64)4) << 32;
191         /* The importance ratio between the two terms in the cost function below */
192
193         unsigned int status;
194         unsigned int temp;
195         unsigned int onw_min;
196         unsigned int inw, onw, inw_best = 0;
197         /* number of pixels in the left stripe NOT hidden by the right stripe */
198         u64 irr_opt; /* the optimal inverse resizing ratio */
199         u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
200         u64 dinw; /* the misalignment between the stripes */
201         /* (measured in units of input columns) */
202         u64 difwl, difwr;
203         /* The number of input columns not reflected in the output */
204         /* the resizing ratio used for the right stripe is */
205         /*   left->irr and right->irr respectively */
206         u64 cost, cost_min;
207         u64 div; /* result of division */
208
209         unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
210
211         status = 0;
212
213         /* M, F calculations */
214         /* read back pfs from params */
215
216         input_f = f_calc(input_pixelformat, 0, NULL);
217         input_m = 16;
218         /* BPP should be used in the out_F calc */
219         /* Temporarily not used */
220         /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */
221
222         output_f = 16;
223         output_m = m_calc(output_pixelformat);
224
225
226         if ((input_frame_width < 4) || (output_frame_width < 4))
227                 return 1;
228
229         irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32),
230                           (output_frame_width - 1));
231         rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32),
232                          (input_frame_width - 1));
233
234         if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
235             || (input_frame_width < (2 * input_f))
236             || ((((u64)output_frame_width) << 32) <
237                 (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
238             || (maximal_stripe_width < output_f)
239             || (output_frame_width <= maximal_stripe_width)
240             || ((2 * maximal_stripe_width) < output_frame_width))
241                 return 1;
242
243         if (output_f % output_m)
244                 status += 2;
245
246         temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
247         if (temp < output_frame_width) {
248                 output_frame_width = temp;
249                 status += 4;
250         }
251
252         if (equal_stripes) {
253                 if ((irr_opt > cirr) /* overlap in the input is not requested */
254                     && ((input_frame_width % (input_m << 1)) == 0)
255                     && ((input_frame_width % (input_f << 1)) == 0)
256                     && ((output_frame_width % (output_m << 1)) == 0)
257                     && ((output_frame_width % (output_f << 1)) == 0)) {
258                         /* without overlap */
259                         left->input_width = right->input_width = right->input_column =
260                                 input_frame_width >> 1;
261                         left->output_width = right->output_width = right->output_column =
262                                 output_frame_width >> 1;
263                         left->input_column = 0;
264                         div = _do_div(((((u64)irr_steps) << 32) *
265                                        (right->input_width - 1)), (right->output_width - 1));
266                         left->irr = right->irr = truncate(0, div, 1);
267                 } else { /* with overlap */
268                         onw = truncate(0, (((u64)output_frame_width - 1) << 32) >> 1,
269                                        output_f);
270                         inw = truncate(0, onw * irr_opt, input_f);
271                         /* this is the maximal inw which allows the same resizing ratio */
272                         /* in both stripes */
273                         onw = truncate(1, (inw * rr_opt), output_f);
274                         div = _do_div((((u64)(irr_steps * inw)) <<
275                                        32), onw);
276                         left->irr = right->irr = truncate(0, div, 1);
277                         left->output_width = right->output_width =
278                                 output_frame_width - onw;
279                         /* These are valid assignments for output_width, */
280                         /* assuming output_f is a multiple of output_m */
281                         div = (((u64)(left->output_width-1) * (left->irr)) << 32);
282                         div = (((u64)1) << 32) + _do_div(div, irr_steps);
283
284                         left->input_width = right->input_width = truncate(1, div, input_m);
285
286                         div = _do_div((((u64)((right->output_width - 1) * right->irr)) <<
287                                        32), irr_steps);
288                         difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
289                         div = _do_div((difwr + (((u64)input_f) << 32)), 2);
290                         left->input_column = truncate(0, div, input_f);
291
292
293                         /* This splits the truncated input columns evenly */
294                         /*    between the left and right margins */
295                         right->input_column = left->input_column + inw;
296                         left->output_column = 0;
297                         right->output_column = onw;
298                 }
299         } else { /* independent stripes */
300                 onw_min = output_frame_width - maximal_stripe_width;
301                 /* onw is a multiple of output_f, in the range */
302                 /* [max(output_f,output_frame_width-maximal_stripe_width),*/
303                 /*min(output_frame_width-2,maximal_stripe_width)] */
304                 /* definitely beyond the cost of any valid setting */
305                 cost_min = (((u64)input_frame_width) << 32) + cr;
306                 onw = truncate(0, ((u64)maximal_stripe_width), output_f);
307                 if (output_frame_width - onw == 1)
308                         onw -= output_f; /*  => onw and output_frame_width-1-onw are positive */
309                 inw = truncate(0, onw * irr_opt, input_f);
310                 /* this is the maximal inw which allows the same resizing ratio */
311                 /* in both stripes */
312                 onw = truncate(1, inw * rr_opt, output_f);
313                 do {
314                         div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
315                         left->irr = truncate(0, div, 1);
316                         div = _do_div((((u64)(onw * left->irr)) << 32),
317                                       irr_steps);
318                         dinw = (((u64)inw) << 32) - div;
319
320                         div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
321                                        32), irr_steps);
322
323                         difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
324
325                         cost = difwl + (((u64)(cr * dinw)) >> 32);
326
327                         if (cost < cost_min) {
328                                 inw_best = inw;
329                                 cost_min = cost;
330                         }
331
332                         inw -= input_f;
333                         onw = truncate(1, inw * rr_opt, output_f);
334                         /* This is the minimal onw which allows the same resizing ratio */
335                         /*     in both stripes */
336                 } while (onw >= onw_min);
337
338                 inw = inw_best;
339                 onw = truncate(1, inw * rr_opt, output_f);
340                 div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
341                 left->irr = truncate(0, div, 1);
342
343                 left->output_width = onw;
344                 right->output_width = output_frame_width - onw;
345                 /* These are valid assignments for output_width, */
346                 /* assuming output_f is a multiple of output_m */
347                 left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
348                 right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
349                                               32, input_m);
350
351                 div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
352                                32), (right->output_width - 1));
353                 right->irr = truncate(0, div, 1);
354                 temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
355                 if (temp < right->irr)
356                         right->irr = temp;
357                 div = _do_div(((u64)((right->output_width - 1) * right->irr) <<
358                                32), irr_steps);
359                 difwr = (u64)(input_frame_width - 1 - inw) - div;
360
361
362                 div = _do_div((difwr + (((u64)input_f) << 32)), 2);
363                 left->input_column = truncate(0, div, input_f);
364
365                 /* This splits the truncated input columns evenly */
366                 /*    between the left and right margins */
367                 right->input_column = left->input_column + inw;
368                 left->output_column = 0;
369                 right->output_column = onw;
370         }
371         return status;
372 }
373 EXPORT_SYMBOL(ipu_calc_stripes_sizes);