]> Pileus Git - ~andy/linux/blob - drivers/media/video/omap3isp/ispresizer.c
829d7bfd422df9fd8a9ce6747033276694bcf3e9
[~andy/linux] / drivers / media / video / omap3isp / ispresizer.c
1 /*
2  * ispresizer.c
3  *
4  * TI OMAP3 ISP - Resizer module
5  *
6  * Copyright (C) 2010 Nokia Corporation
7  * Copyright (C) 2009 Texas Instruments, Inc
8  *
9  * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
10  *           Sakari Ailus <sakari.ailus@iki.fi>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  */
26
27 #include <linux/device.h>
28 #include <linux/mm.h>
29 #include <linux/module.h>
30
31 #include "isp.h"
32 #include "ispreg.h"
33 #include "ispresizer.h"
34
35 /*
36  * Resizer Constants
37  */
38 #define MIN_RESIZE_VALUE                64
39 #define MID_RESIZE_VALUE                512
40 #define MAX_RESIZE_VALUE                1024
41
42 #define MIN_IN_WIDTH                    32
43 #define MIN_IN_HEIGHT                   32
44 #define MAX_IN_WIDTH_MEMORY_MODE        4095
45 #define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1  1280
46 #define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2  4095
47 #define MAX_IN_HEIGHT                   4095
48
49 #define MIN_OUT_WIDTH                   16
50 #define MIN_OUT_HEIGHT                  2
51 #define MAX_OUT_HEIGHT                  4095
52
53 /*
54  * Resizer Use Constraints
55  * "TRM ES3.1, table 12-46"
56  */
57 #define MAX_4TAP_OUT_WIDTH_ES1          1280
58 #define MAX_7TAP_OUT_WIDTH_ES1          640
59 #define MAX_4TAP_OUT_WIDTH_ES2          3312
60 #define MAX_7TAP_OUT_WIDTH_ES2          1650
61 #define MAX_4TAP_OUT_WIDTH_3630         4096
62 #define MAX_7TAP_OUT_WIDTH_3630         2048
63
64 /*
65  * Constants for ratio calculation
66  */
67 #define RESIZE_DIVISOR                  256
68 #define DEFAULT_PHASE                   1
69
70 /*
71  * Default (and only) configuration of filter coefficients.
72  * 7-tap mode is for scale factors 0.25x to 0.5x.
73  * 4-tap mode is for scale factors 0.5x to 4.0x.
74  * There shouldn't be any reason to recalculate these, EVER.
75  */
76 static const struct isprsz_coef filter_coefs = {
77         /* For 8-phase 4-tap horizontal filter: */
78         {
79                 0x0000, 0x0100, 0x0000, 0x0000,
80                 0x03FA, 0x00F6, 0x0010, 0x0000,
81                 0x03F9, 0x00DB, 0x002C, 0x0000,
82                 0x03FB, 0x00B3, 0x0053, 0x03FF,
83                 0x03FD, 0x0082, 0x0084, 0x03FD,
84                 0x03FF, 0x0053, 0x00B3, 0x03FB,
85                 0x0000, 0x002C, 0x00DB, 0x03F9,
86                 0x0000, 0x0010, 0x00F6, 0x03FA
87         },
88         /* For 8-phase 4-tap vertical filter: */
89         {
90                 0x0000, 0x0100, 0x0000, 0x0000,
91                 0x03FA, 0x00F6, 0x0010, 0x0000,
92                 0x03F9, 0x00DB, 0x002C, 0x0000,
93                 0x03FB, 0x00B3, 0x0053, 0x03FF,
94                 0x03FD, 0x0082, 0x0084, 0x03FD,
95                 0x03FF, 0x0053, 0x00B3, 0x03FB,
96                 0x0000, 0x002C, 0x00DB, 0x03F9,
97                 0x0000, 0x0010, 0x00F6, 0x03FA
98         },
99         /* For 4-phase 7-tap horizontal filter: */
100         #define DUMMY 0
101         {
102                 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
103                 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
104                 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
105                 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
106         },
107         /* For 4-phase 7-tap vertical filter: */
108         {
109                 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
110                 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
111                 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
112                 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
113         }
114         /*
115          * The dummy padding is required in 7-tap mode because of how the
116          * registers are arranged physically.
117          */
118         #undef DUMMY
119 };
120
121 /*
122  * __resizer_get_format - helper function for getting resizer format
123  * @res   : pointer to resizer private structure
124  * @pad   : pad number
125  * @fh    : V4L2 subdev file handle
126  * @which : wanted subdev format
127  * return zero
128  */
129 static struct v4l2_mbus_framefmt *
130 __resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
131                      unsigned int pad, enum v4l2_subdev_format_whence which)
132 {
133         if (which == V4L2_SUBDEV_FORMAT_TRY)
134                 return v4l2_subdev_get_try_format(fh, pad);
135         else
136                 return &res->formats[pad];
137 }
138
139 /*
140  * __resizer_get_crop - helper function for getting resizer crop rectangle
141  * @res   : pointer to resizer private structure
142  * @fh    : V4L2 subdev file handle
143  * @which : wanted subdev crop rectangle
144  */
145 static struct v4l2_rect *
146 __resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
147                    enum v4l2_subdev_format_whence which)
148 {
149         if (which == V4L2_SUBDEV_FORMAT_TRY)
150                 return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK);
151         else
152                 return &res->crop.request;
153 }
154
155 /*
156  * resizer_set_filters - Set resizer filters
157  * @res: Device context.
158  * @h_coeff: horizontal coefficient
159  * @v_coeff: vertical coefficient
160  * Return none
161  */
162 static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff,
163                                 const u16 *v_coeff)
164 {
165         struct isp_device *isp = to_isp_device(res);
166         u32 startaddr_h, startaddr_v, tmp_h, tmp_v;
167         int i;
168
169         startaddr_h = ISPRSZ_HFILT10;
170         startaddr_v = ISPRSZ_VFILT10;
171
172         for (i = 0; i < COEFF_CNT; i += 2) {
173                 tmp_h = h_coeff[i] |
174                         (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT);
175                 tmp_v = v_coeff[i] |
176                         (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT);
177                 isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h);
178                 isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v);
179                 startaddr_h += 4;
180                 startaddr_v += 4;
181         }
182 }
183
184 /*
185  * resizer_set_bilinear - Chrominance horizontal algorithm select
186  * @res: Device context.
187  * @type: Filtering interpolation type.
188  *
189  * Filtering that is same as luminance processing is
190  * intended only for downsampling, and bilinear interpolation
191  * is intended only for upsampling.
192  */
193 static void resizer_set_bilinear(struct isp_res_device *res,
194                                  enum resizer_chroma_algo type)
195 {
196         struct isp_device *isp = to_isp_device(res);
197
198         if (type == RSZ_BILINEAR)
199                 isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
200                             ISPRSZ_CNT_CBILIN);
201         else
202                 isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
203                             ISPRSZ_CNT_CBILIN);
204 }
205
206 /*
207  * resizer_set_ycpos - Luminance and chrominance order
208  * @res: Device context.
209  * @order: order type.
210  */
211 static void resizer_set_ycpos(struct isp_res_device *res,
212                               enum v4l2_mbus_pixelcode pixelcode)
213 {
214         struct isp_device *isp = to_isp_device(res);
215
216         switch (pixelcode) {
217         case V4L2_MBUS_FMT_YUYV8_1X16:
218                 isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
219                             ISPRSZ_CNT_YCPOS);
220                 break;
221         case V4L2_MBUS_FMT_UYVY8_1X16:
222                 isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
223                             ISPRSZ_CNT_YCPOS);
224                 break;
225         default:
226                 return;
227         }
228 }
229
230 /*
231  * resizer_set_phase - Setup horizontal and vertical starting phase
232  * @res: Device context.
233  * @h_phase: horizontal phase parameters.
234  * @v_phase: vertical phase parameters.
235  *
236  * Horizontal and vertical phase range is 0 to 7
237  */
238 static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
239                               u32 v_phase)
240 {
241         struct isp_device *isp = to_isp_device(res);
242         u32 rgval = 0;
243
244         rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
245               ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
246         rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK;
247         rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK;
248
249         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
250 }
251
252 /*
253  * resizer_set_luma - Setup luminance enhancer parameters
254  * @res: Device context.
255  * @luma: Structure for luminance enhancer parameters.
256  *
257  * Algorithm select:
258  *  0x0: Disable
259  *  0x1: [-1  2 -1]/2 high-pass filter
260  *  0x2: [-1 -2  6 -2 -1]/4 high-pass filter
261  *
262  * Maximum gain:
263  *  The data is coded in U4Q4 representation.
264  *
265  * Slope:
266  *  The data is coded in U4Q4 representation.
267  *
268  * Coring offset:
269  *  The data is coded in U8Q0 representation.
270  *
271  * The new luminance value is computed as:
272  *  Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4.
273  */
274 static void resizer_set_luma(struct isp_res_device *res,
275                              struct resizer_luma_yenh *luma)
276 {
277         struct isp_device *isp = to_isp_device(res);
278         u32 rgval = 0;
279
280         rgval  = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
281                   & ISPRSZ_YENH_ALGO_MASK;
282         rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT)
283                   & ISPRSZ_YENH_GAIN_MASK;
284         rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT)
285                   & ISPRSZ_YENH_SLOP_MASK;
286         rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT)
287                   & ISPRSZ_YENH_CORE_MASK;
288
289         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH);
290 }
291
292 /*
293  * resizer_set_source - Input source select
294  * @res: Device context.
295  * @source: Input source type
296  *
297  * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from
298  * Preview/CCDC engine, otherwise from memory.
299  */
300 static void resizer_set_source(struct isp_res_device *res,
301                                enum resizer_input_entity source)
302 {
303         struct isp_device *isp = to_isp_device(res);
304
305         if (source == RESIZER_INPUT_MEMORY)
306                 isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
307                             ISPRSZ_CNT_INPSRC);
308         else
309                 isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
310                             ISPRSZ_CNT_INPSRC);
311 }
312
313 /*
314  * resizer_set_ratio - Setup horizontal and vertical resizing value
315  * @res: Device context.
316  * @ratio: Structure for ratio parameters.
317  *
318  * Resizing range from 64 to 1024
319  */
320 static void resizer_set_ratio(struct isp_res_device *res,
321                               const struct resizer_ratio *ratio)
322 {
323         struct isp_device *isp = to_isp_device(res);
324         const u16 *h_filter, *v_filter;
325         u32 rgval = 0;
326
327         rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
328                               ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
329         rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT)
330                   & ISPRSZ_CNT_HRSZ_MASK;
331         rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT)
332                   & ISPRSZ_CNT_VRSZ_MASK;
333         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
334
335         /* prepare horizontal filter coefficients */
336         if (ratio->horz > MID_RESIZE_VALUE)
337                 h_filter = &filter_coefs.h_filter_coef_7tap[0];
338         else
339                 h_filter = &filter_coefs.h_filter_coef_4tap[0];
340
341         /* prepare vertical filter coefficients */
342         if (ratio->vert > MID_RESIZE_VALUE)
343                 v_filter = &filter_coefs.v_filter_coef_7tap[0];
344         else
345                 v_filter = &filter_coefs.v_filter_coef_4tap[0];
346
347         resizer_set_filters(res, h_filter, v_filter);
348 }
349
350 /*
351  * resizer_set_dst_size - Setup the output height and width
352  * @res: Device context.
353  * @width: Output width.
354  * @height: Output height.
355  *
356  * Width :
357  *  The value must be EVEN.
358  *
359  * Height:
360  *  The number of bytes written to SDRAM must be
361  *  a multiple of 16-bytes if the vertical resizing factor
362  *  is greater than 1x (upsizing)
363  */
364 static void resizer_set_output_size(struct isp_res_device *res,
365                                     u32 width, u32 height)
366 {
367         struct isp_device *isp = to_isp_device(res);
368         u32 rgval = 0;
369
370         dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
371         rgval  = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
372                  & ISPRSZ_OUT_SIZE_HORZ_MASK;
373         rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
374                  & ISPRSZ_OUT_SIZE_VERT_MASK;
375         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE);
376 }
377
378 /*
379  * resizer_set_output_offset - Setup memory offset for the output lines.
380  * @res: Device context.
381  * @offset: Memory offset.
382  *
383  * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
384  * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth,
385  * the SDRAM line offset must be set on a 256-byte boundary
386  */
387 static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
388 {
389         struct isp_device *isp = to_isp_device(res);
390
391         isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF);
392 }
393
394 /*
395  * resizer_set_start - Setup vertical and horizontal start position
396  * @res: Device context.
397  * @left: Horizontal start position.
398  * @top: Vertical start position.
399  *
400  * Vertical start line:
401  *  This field makes sense only when the resizer obtains its input
402  *  from the preview engine/CCDC
403  *
404  * Horizontal start pixel:
405  *  Pixels are coded on 16 bits for YUV and 8 bits for color separate data.
406  *  When the resizer gets its input from SDRAM, this field must be set
407  *  to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data
408  */
409 static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
410 {
411         struct isp_device *isp = to_isp_device(res);
412         u32 rgval = 0;
413
414         rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
415                 & ISPRSZ_IN_START_HORZ_ST_MASK;
416         rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT)
417                  & ISPRSZ_IN_START_VERT_ST_MASK;
418
419         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START);
420 }
421
422 /*
423  * resizer_set_input_size - Setup the input size
424  * @res: Device context.
425  * @width: The range is 0 to 4095 pixels
426  * @height: The range is 0 to 4095 lines
427  */
428 static void resizer_set_input_size(struct isp_res_device *res,
429                                    u32 width, u32 height)
430 {
431         struct isp_device *isp = to_isp_device(res);
432         u32 rgval = 0;
433
434         dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
435
436         rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
437                 & ISPRSZ_IN_SIZE_HORZ_MASK;
438         rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT)
439                  & ISPRSZ_IN_SIZE_VERT_MASK;
440
441         isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE);
442 }
443
444 /*
445  * resizer_set_src_offs - Setup the memory offset for the input lines
446  * @res: Device context.
447  * @offset: Memory offset.
448  *
449  * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
450  * boundary; the 5 LSBs are read-only. This field must be programmed to be
451  * 0x0 if the resizer input is from preview engine/CCDC.
452  */
453 static void resizer_set_input_offset(struct isp_res_device *res, u32 offset)
454 {
455         struct isp_device *isp = to_isp_device(res);
456
457         isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF);
458 }
459
460 /*
461  * resizer_set_intype - Input type select
462  * @res: Device context.
463  * @type: Pixel format type.
464  */
465 static void resizer_set_intype(struct isp_res_device *res,
466                                enum resizer_colors_type type)
467 {
468         struct isp_device *isp = to_isp_device(res);
469
470         if (type == RSZ_COLOR8)
471                 isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
472                             ISPRSZ_CNT_INPTYP);
473         else
474                 isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
475                             ISPRSZ_CNT_INPTYP);
476 }
477
478 /*
479  * __resizer_set_inaddr - Helper function for set input address
480  * @res : pointer to resizer private data structure
481  * @addr: input address
482  * return none
483  */
484 static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr)
485 {
486         struct isp_device *isp = to_isp_device(res);
487
488         isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD);
489 }
490
491 /*
492  * The data rate at the horizontal resizer output must not exceed half the
493  * functional clock or 100 MP/s, whichever is lower. According to the TRM
494  * there's no similar requirement for the vertical resizer output. However
495  * experience showed that vertical upscaling by 4 leads to SBL overflows (with
496  * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer
497  * output data rate to the functional clock or 200 MP/s, whichever is lower,
498  * seems to get rid of SBL overflows.
499  *
500  * The maximum data rate at the output of the horizontal resizer can thus be
501  * computed with
502  *
503  * max intermediate rate <= L3 clock * input height / output height
504  * max intermediate rate <= L3 clock / 2
505  *
506  * The maximum data rate at the resizer input is then
507  *
508  * max input rate <= max intermediate rate * input width / output width
509  *
510  * where the input width and height are the resizer input crop rectangle size.
511  * The TRM doesn't clearly explain if that's a maximum instant data rate or a
512  * maximum average data rate.
513  */
514 void omap3isp_resizer_max_rate(struct isp_res_device *res,
515                                unsigned int *max_rate)
516 {
517         struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
518         const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE];
519         unsigned long limit = min(pipe->l3_ick, 200000000UL);
520         unsigned long clock;
521
522         clock = div_u64((u64)limit * res->crop.active.height, ofmt->height);
523         clock = min(clock, limit / 2);
524         *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width);
525 }
526
527 /*
528  * When the resizer processes images from memory, the driver must slow down read
529  * requests on the input to at least comply with the internal data rate
530  * requirements. If the application real-time requirements can cope with slower
531  * processing, the resizer can be slowed down even more to put less pressure on
532  * the overall system.
533  *
534  * When the resizer processes images on the fly (either from the CCDC or the
535  * preview module), the same data rate requirements apply but they can't be
536  * enforced at the resizer level. The image input module (sensor, CCP2 or
537  * preview module) must not provide image data faster than the resizer can
538  * process.
539  *
540  * For live image pipelines, the data rate is set by the frame format, size and
541  * rate. The sensor output frame rate must not exceed the maximum resizer data
542  * rate.
543  *
544  * The resizer slows down read requests by inserting wait cycles in the SBL
545  * requests. The maximum number of 256-byte requests per second can be computed
546  * as (the data rate is multiplied by 2 to convert from pixels per second to
547  * bytes per second)
548  *
549  * request per second = data rate * 2 / 256
550  * cycles per request = cycles per second / requests per second
551  *
552  * The number of cycles per second is controlled by the L3 clock, leading to
553  *
554  * cycles per request = L3 frequency / 2 * 256 / data rate
555  */
556 static void resizer_adjust_bandwidth(struct isp_res_device *res)
557 {
558         struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
559         struct isp_device *isp = to_isp_device(res);
560         unsigned long l3_ick = pipe->l3_ick;
561         struct v4l2_fract *timeperframe;
562         unsigned int cycles_per_frame;
563         unsigned int requests_per_frame;
564         unsigned int cycles_per_request;
565         unsigned int granularity;
566         unsigned int minimum;
567         unsigned int maximum;
568         unsigned int value;
569
570         if (res->input != RESIZER_INPUT_MEMORY) {
571                 isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
572                             ISPSBL_SDR_REQ_RSZ_EXP_MASK);
573                 return;
574         }
575
576         switch (isp->revision) {
577         case ISP_REVISION_1_0:
578         case ISP_REVISION_2_0:
579         default:
580                 granularity = 1024;
581                 break;
582
583         case ISP_REVISION_15_0:
584                 granularity = 32;
585                 break;
586         }
587
588         /* Compute the minimum number of cycles per request, based on the
589          * pipeline maximum data rate. This is an absolute lower bound if we
590          * don't want SBL overflows, so round the value up.
591          */
592         cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
593                                      pipe->max_rate);
594         minimum = DIV_ROUND_UP(cycles_per_request, granularity);
595
596         /* Compute the maximum number of cycles per request, based on the
597          * requested frame rate. This is a soft upper bound to achieve a frame
598          * rate equal or higher than the requested value, so round the value
599          * down.
600          */
601         timeperframe = &pipe->max_timeperframe;
602
603         requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256)
604                            * res->crop.active.height;
605         cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
606                                    timeperframe->denominator);
607         cycles_per_request = cycles_per_frame / requests_per_frame;
608
609         maximum = cycles_per_request / granularity;
610
611         value = max(minimum, maximum);
612
613         dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
614         isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
615                         ISPSBL_SDR_REQ_RSZ_EXP_MASK,
616                         value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT);
617 }
618
619 /*
620  * omap3isp_resizer_busy - Checks if ISP resizer is busy.
621  *
622  * Returns busy field from ISPRSZ_PCR register.
623  */
624 int omap3isp_resizer_busy(struct isp_res_device *res)
625 {
626         struct isp_device *isp = to_isp_device(res);
627
628         return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) &
629                              ISPRSZ_PCR_BUSY;
630 }
631
632 /*
633  * resizer_set_inaddr - Sets the memory address of the input frame.
634  * @addr: 32bit memory address aligned on 32byte boundary.
635  */
636 static void resizer_set_inaddr(struct isp_res_device *res, u32 addr)
637 {
638         res->addr_base = addr;
639
640         /* This will handle crop settings in stream off state */
641         if (res->crop_offset)
642                 addr += res->crop_offset & ~0x1f;
643
644         __resizer_set_inaddr(res, addr);
645 }
646
647 /*
648  * Configures the memory address to which the output frame is written.
649  * @addr: 32bit memory address aligned on 32byte boundary.
650  * Note: For SBL efficiency reasons the address should be on a 256-byte
651  * boundary.
652  */
653 static void resizer_set_outaddr(struct isp_res_device *res, u32 addr)
654 {
655         struct isp_device *isp = to_isp_device(res);
656
657         /*
658          * Set output address. This needs to be in its own function
659          * because it changes often.
660          */
661         isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT,
662                        OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD);
663 }
664
665 /*
666  * resizer_print_status - Prints the values of the resizer module registers.
667  */
668 #define RSZ_PRINT_REGISTER(isp, name)\
669         dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \
670                 isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name))
671
672 static void resizer_print_status(struct isp_res_device *res)
673 {
674         struct isp_device *isp = to_isp_device(res);
675
676         dev_dbg(isp->dev, "-------------Resizer Register dump----------\n");
677
678         RSZ_PRINT_REGISTER(isp, PCR);
679         RSZ_PRINT_REGISTER(isp, CNT);
680         RSZ_PRINT_REGISTER(isp, OUT_SIZE);
681         RSZ_PRINT_REGISTER(isp, IN_START);
682         RSZ_PRINT_REGISTER(isp, IN_SIZE);
683         RSZ_PRINT_REGISTER(isp, SDR_INADD);
684         RSZ_PRINT_REGISTER(isp, SDR_INOFF);
685         RSZ_PRINT_REGISTER(isp, SDR_OUTADD);
686         RSZ_PRINT_REGISTER(isp, SDR_OUTOFF);
687         RSZ_PRINT_REGISTER(isp, YENH);
688
689         dev_dbg(isp->dev, "--------------------------------------------\n");
690 }
691
692 /*
693  * resizer_calc_ratios - Helper function for calculate resizer ratios
694  * @res: pointer to resizer private data structure
695  * @input: input frame size
696  * @output: output frame size
697  * @ratio : return calculated ratios
698  * return none
699  *
700  * The resizer uses a polyphase sample rate converter. The upsampling filter
701  * has a fixed number of phases that depend on the resizing ratio. As the ratio
702  * computation depends on the number of phases, we need to compute a first
703  * approximation and then refine it.
704  *
705  * The input/output/ratio relationship is given by the OMAP34xx TRM:
706  *
707  * - 8-phase, 4-tap mode (RSZ = 64 ~ 512)
708  *      iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7
709  *      ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4
710  * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024)
711  *      iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7
712  *      ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7
713  *
714  * iw and ih are the input width and height after cropping. Those equations need
715  * to be satisfied exactly for the resizer to work correctly.
716  *
717  * Reverting the equations, we can compute the resizing ratios with
718  *
719  * - 8-phase, 4-tap mode
720  *      hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1)
721  *      vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1)
722  * - 4-phase, 7-tap mode
723  *      hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1)
724  *      vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1)
725  *
726  * The ratios are integer values, and must be rounded down to ensure that the
727  * cropped input size is not bigger than the uncropped input size. As the ratio
728  * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the
729  * 7-tap mode equations to compute a ratio approximation.
730  *
731  * We first clamp the output size according to the hardware capabilitie to avoid
732  * auto-cropping the input more than required to satisfy the TRM equations. The
733  * minimum output size is achieved with a scaling factor of 1024. It is thus
734  * computed using the 7-tap equations.
735  *
736  *      min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1
737  *      min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1
738  *
739  * Similarly, the maximum output size is achieved with a scaling factor of 64
740  * and computed using the 4-tap equations.
741  *
742  *      max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1
743  *      max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1
744  *
745  * The additional +255 term compensates for the round down operation performed
746  * by the TRM equations when shifting the value right by 8 bits.
747  *
748  * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to
749  * the maximum value guarantees that the ratio value will never be smaller than
750  * the minimum, but it could still slightly exceed the maximum. Clamping the
751  * ratio will thus result in a resizing factor slightly larger than the
752  * requested value.
753  *
754  * To accomodate that, and make sure the TRM equations are satisfied exactly, we
755  * compute the input crop rectangle as the last step.
756  *
757  * As if the situation wasn't complex enough, the maximum output width depends
758  * on the vertical resizing ratio.  Fortunately, the output height doesn't
759  * depend on the horizontal resizing ratio. We can then start by computing the
760  * output height and the vertical ratio, and then move to computing the output
761  * width and the horizontal ratio.
762  */
763 static void resizer_calc_ratios(struct isp_res_device *res,
764                                 struct v4l2_rect *input,
765                                 struct v4l2_mbus_framefmt *output,
766                                 struct resizer_ratio *ratio)
767 {
768         struct isp_device *isp = to_isp_device(res);
769         const unsigned int spv = DEFAULT_PHASE;
770         const unsigned int sph = DEFAULT_PHASE;
771         unsigned int upscaled_width;
772         unsigned int upscaled_height;
773         unsigned int min_width;
774         unsigned int min_height;
775         unsigned int max_width;
776         unsigned int max_height;
777         unsigned int width_alignment;
778         unsigned int width;
779         unsigned int height;
780
781         /*
782          * Clamp the output height based on the hardware capabilities and
783          * compute the vertical resizing ratio.
784          */
785         min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1;
786         min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT);
787         max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1;
788         max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
789         output->height = clamp(output->height, min_height, max_height);
790
791         ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv)
792                     / (output->height - 1);
793         ratio->vert = clamp_t(unsigned int, ratio->vert,
794                               MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
795
796         if (ratio->vert <= MID_RESIZE_VALUE) {
797                 upscaled_height = (output->height - 1) * ratio->vert
798                                 + 32 * spv + 16;
799                 height = (upscaled_height >> 8) + 4;
800         } else {
801                 upscaled_height = (output->height - 1) * ratio->vert
802                                 + 64 * spv + 32;
803                 height = (upscaled_height >> 8) + 7;
804         }
805
806         /*
807          * Compute the minimum and maximum output widths based on the hardware
808          * capabilities. The maximum depends on the vertical resizing ratio.
809          */
810         min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1;
811         min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH);
812
813         if (ratio->vert <= MID_RESIZE_VALUE) {
814                 switch (isp->revision) {
815                 case ISP_REVISION_1_0:
816                         max_width = MAX_4TAP_OUT_WIDTH_ES1;
817                         break;
818
819                 case ISP_REVISION_2_0:
820                 default:
821                         max_width = MAX_4TAP_OUT_WIDTH_ES2;
822                         break;
823
824                 case ISP_REVISION_15_0:
825                         max_width = MAX_4TAP_OUT_WIDTH_3630;
826                         break;
827                 }
828         } else {
829                 switch (isp->revision) {
830                 case ISP_REVISION_1_0:
831                         max_width = MAX_7TAP_OUT_WIDTH_ES1;
832                         break;
833
834                 case ISP_REVISION_2_0:
835                 default:
836                         max_width = MAX_7TAP_OUT_WIDTH_ES2;
837                         break;
838
839                 case ISP_REVISION_15_0:
840                         max_width = MAX_7TAP_OUT_WIDTH_3630;
841                         break;
842                 }
843         }
844         max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64
845                         + 1, max_width);
846
847         /*
848          * The output width must be even, and must be a multiple of 16 bytes
849          * when upscaling vertically. Clamp the output width to the valid range.
850          * Take the alignment into account (the maximum width in 7-tap mode on
851          * ES2 isn't a multiple of 8) and align the result up to make sure it
852          * won't be smaller than the minimum.
853          */
854         width_alignment = ratio->vert < 256 ? 8 : 2;
855         output->width = clamp(output->width, min_width,
856                               max_width & ~(width_alignment - 1));
857         output->width = ALIGN(output->width, width_alignment);
858
859         ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph)
860                     / (output->width - 1);
861         ratio->horz = clamp_t(unsigned int, ratio->horz,
862                               MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
863
864         if (ratio->horz <= MID_RESIZE_VALUE) {
865                 upscaled_width = (output->width - 1) * ratio->horz
866                                + 32 * sph + 16;
867                 width = (upscaled_width >> 8) + 7;
868         } else {
869                 upscaled_width = (output->width - 1) * ratio->horz
870                                + 64 * sph + 32;
871                 width = (upscaled_width >> 8) + 7;
872         }
873
874         /* Center the new crop rectangle. */
875         input->left += (input->width - width) / 2;
876         input->top += (input->height - height) / 2;
877         input->width = width;
878         input->height = height;
879 }
880
881 /*
882  * resizer_set_crop_params - Setup hardware with cropping parameters
883  * @res : resizer private structure
884  * @crop_rect : current crop rectangle
885  * @ratio : resizer ratios
886  * return none
887  */
888 static void resizer_set_crop_params(struct isp_res_device *res,
889                                     const struct v4l2_mbus_framefmt *input,
890                                     const struct v4l2_mbus_framefmt *output)
891 {
892         resizer_set_ratio(res, &res->ratio);
893
894         /* Set chrominance horizontal algorithm */
895         if (res->ratio.horz >= RESIZE_DIVISOR)
896                 resizer_set_bilinear(res, RSZ_THE_SAME);
897         else
898                 resizer_set_bilinear(res, RSZ_BILINEAR);
899
900         resizer_adjust_bandwidth(res);
901
902         if (res->input == RESIZER_INPUT_MEMORY) {
903                 /* Calculate additional offset for crop */
904                 res->crop_offset = (res->crop.active.top * input->width +
905                                     res->crop.active.left) * 2;
906                 /*
907                  * Write lowest 4 bits of horizontal pixel offset (in pixels),
908                  * vertical start must be 0.
909                  */
910                 resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0);
911
912                 /*
913                  * Set start (read) address for cropping, in bytes.
914                  * Lowest 5 bits must be zero.
915                  */
916                 __resizer_set_inaddr(res,
917                                 res->addr_base + (res->crop_offset & ~0x1f));
918         } else {
919                 /*
920                  * Set vertical start line and horizontal starting pixel.
921                  * If the input is from CCDC/PREV, horizontal start field is
922                  * in bytes (twice number of pixels).
923                  */
924                 resizer_set_start(res, res->crop.active.left * 2,
925                                   res->crop.active.top);
926                 /* Input address and offset must be 0 for preview/ccdc input */
927                 __resizer_set_inaddr(res, 0);
928                 resizer_set_input_offset(res, 0);
929         }
930
931         /* Set the input size */
932         resizer_set_input_size(res, res->crop.active.width,
933                                res->crop.active.height);
934 }
935
936 static void resizer_configure(struct isp_res_device *res)
937 {
938         struct v4l2_mbus_framefmt *informat, *outformat;
939         struct resizer_luma_yenh luma = {0, 0, 0, 0};
940
941         resizer_set_source(res, res->input);
942
943         informat = &res->formats[RESZ_PAD_SINK];
944         outformat = &res->formats[RESZ_PAD_SOURCE];
945
946         /* RESZ_PAD_SINK */
947         if (res->input == RESIZER_INPUT_VP)
948                 resizer_set_input_offset(res, 0);
949         else
950                 resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2);
951
952         /* YUV422 interleaved, default phase, no luma enhancement */
953         resizer_set_intype(res, RSZ_YUV422);
954         resizer_set_ycpos(res, informat->code);
955         resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE);
956         resizer_set_luma(res, &luma);
957
958         /* RESZ_PAD_SOURCE */
959         resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32));
960         resizer_set_output_size(res, outformat->width, outformat->height);
961
962         resizer_set_crop_params(res, informat, outformat);
963 }
964
965 /* -----------------------------------------------------------------------------
966  * Interrupt handling
967  */
968
969 static void resizer_enable_oneshot(struct isp_res_device *res)
970 {
971         struct isp_device *isp = to_isp_device(res);
972
973         isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR,
974                     ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT);
975 }
976
977 void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res)
978 {
979         /*
980          * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
981          * condition, the module was paused and now we have a buffer queued
982          * on the output again. Restart the pipeline if running in continuous
983          * mode.
984          */
985         if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
986             res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
987                 resizer_enable_oneshot(res);
988                 isp_video_dmaqueue_flags_clr(&res->video_out);
989         }
990 }
991
992 static void resizer_isr_buffer(struct isp_res_device *res)
993 {
994         struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
995         struct isp_buffer *buffer;
996         int restart = 0;
997
998         if (res->state == ISP_PIPELINE_STREAM_STOPPED)
999                 return;
1000
1001         /* Complete the output buffer and, if reading from memory, the input
1002          * buffer.
1003          */
1004         buffer = omap3isp_video_buffer_next(&res->video_out, res->error);
1005         if (buffer != NULL) {
1006                 resizer_set_outaddr(res, buffer->isp_addr);
1007                 restart = 1;
1008         }
1009
1010         pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
1011
1012         if (res->input == RESIZER_INPUT_MEMORY) {
1013                 buffer = omap3isp_video_buffer_next(&res->video_in, 0);
1014                 if (buffer != NULL)
1015                         resizer_set_inaddr(res, buffer->isp_addr);
1016                 pipe->state |= ISP_PIPELINE_IDLE_INPUT;
1017         }
1018
1019         if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
1020                 if (isp_pipeline_ready(pipe))
1021                         omap3isp_pipeline_set_stream(pipe,
1022                                                 ISP_PIPELINE_STREAM_SINGLESHOT);
1023         } else {
1024                 /* If an underrun occurs, the video queue operation handler will
1025                  * restart the resizer. Otherwise restart it immediately.
1026                  */
1027                 if (restart)
1028                         resizer_enable_oneshot(res);
1029         }
1030
1031         res->error = 0;
1032 }
1033
1034 /*
1035  * omap3isp_resizer_isr - ISP resizer interrupt handler
1036  *
1037  * Manage the resizer video buffers and configure shadowed and busy-locked
1038  * registers.
1039  */
1040 void omap3isp_resizer_isr(struct isp_res_device *res)
1041 {
1042         struct v4l2_mbus_framefmt *informat, *outformat;
1043
1044         if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
1045                 return;
1046
1047         if (res->applycrop) {
1048                 outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
1049                                               V4L2_SUBDEV_FORMAT_ACTIVE);
1050                 informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK,
1051                                               V4L2_SUBDEV_FORMAT_ACTIVE);
1052                 resizer_set_crop_params(res, informat, outformat);
1053                 res->applycrop = 0;
1054         }
1055
1056         resizer_isr_buffer(res);
1057 }
1058
1059 /* -----------------------------------------------------------------------------
1060  * ISP video operations
1061  */
1062
1063 static int resizer_video_queue(struct isp_video *video,
1064                                struct isp_buffer *buffer)
1065 {
1066         struct isp_res_device *res = &video->isp->isp_res;
1067
1068         if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
1069                 resizer_set_inaddr(res, buffer->isp_addr);
1070
1071         /*
1072          * We now have a buffer queued on the output. Despite what the
1073          * TRM says, the resizer can't be restarted immediately.
1074          * Enabling it in one shot mode in the middle of a frame (or at
1075          * least asynchronously to the frame) results in the output
1076          * being shifted randomly left/right and up/down, as if the
1077          * hardware didn't synchronize itself to the beginning of the
1078          * frame correctly.
1079          *
1080          * Restart the resizer on the next sync interrupt if running in
1081          * continuous mode or when starting the stream.
1082          */
1083         if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1084                 resizer_set_outaddr(res, buffer->isp_addr);
1085
1086         return 0;
1087 }
1088
1089 static const struct isp_video_operations resizer_video_ops = {
1090         .queue = resizer_video_queue,
1091 };
1092
1093 /* -----------------------------------------------------------------------------
1094  * V4L2 subdev operations
1095  */
1096
1097 /*
1098  * resizer_set_stream - Enable/Disable streaming on resizer subdev
1099  * @sd: ISP resizer V4L2 subdev
1100  * @enable: 1 == Enable, 0 == Disable
1101  *
1102  * The resizer hardware can't be enabled without a memory buffer to write to.
1103  * As the s_stream operation is called in response to a STREAMON call without
1104  * any buffer queued yet, just update the state field and return immediately.
1105  * The resizer will be enabled in resizer_video_queue().
1106  */
1107 static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
1108 {
1109         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1110         struct isp_video *video_out = &res->video_out;
1111         struct isp_device *isp = to_isp_device(res);
1112         struct device *dev = to_device(res);
1113
1114         if (res->state == ISP_PIPELINE_STREAM_STOPPED) {
1115                 if (enable == ISP_PIPELINE_STREAM_STOPPED)
1116                         return 0;
1117
1118                 omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER);
1119                 resizer_configure(res);
1120                 res->error = 0;
1121                 resizer_print_status(res);
1122         }
1123
1124         switch (enable) {
1125         case ISP_PIPELINE_STREAM_CONTINUOUS:
1126                 omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
1127                 if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
1128                         resizer_enable_oneshot(res);
1129                         isp_video_dmaqueue_flags_clr(video_out);
1130                 }
1131                 break;
1132
1133         case ISP_PIPELINE_STREAM_SINGLESHOT:
1134                 if (res->input == RESIZER_INPUT_MEMORY)
1135                         omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ);
1136                 omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
1137
1138                 resizer_enable_oneshot(res);
1139                 break;
1140
1141         case ISP_PIPELINE_STREAM_STOPPED:
1142                 if (omap3isp_module_sync_idle(&sd->entity, &res->wait,
1143                                               &res->stopping))
1144                         dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
1145                 omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ |
1146                                 OMAP3_ISP_SBL_RESIZER_WRITE);
1147                 omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER);
1148                 isp_video_dmaqueue_flags_clr(video_out);
1149                 break;
1150         }
1151
1152         res->state = enable;
1153         return 0;
1154 }
1155
1156 /*
1157  * resizer_g_crop - handle get crop subdev operation
1158  * @sd : pointer to v4l2 subdev structure
1159  * @pad : subdev pad
1160  * @crop : pointer to crop structure
1161  * @which : active or try format
1162  * return zero
1163  */
1164 static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
1165                           struct v4l2_subdev_crop *crop)
1166 {
1167         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1168         struct v4l2_mbus_framefmt *format;
1169         struct resizer_ratio ratio;
1170
1171         /* Only sink pad has crop capability */
1172         if (crop->pad != RESZ_PAD_SINK)
1173                 return -EINVAL;
1174
1175         format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which);
1176         crop->rect = *__resizer_get_crop(res, fh, crop->which);
1177         resizer_calc_ratios(res, &crop->rect, format, &ratio);
1178
1179         return 0;
1180 }
1181
1182 /*
1183  * resizer_try_crop - mangles crop parameters.
1184  */
1185 static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
1186                              const struct v4l2_mbus_framefmt *source,
1187                              struct v4l2_rect *crop)
1188 {
1189         const unsigned int spv = DEFAULT_PHASE;
1190         const unsigned int sph = DEFAULT_PHASE;
1191
1192         /* Crop rectangle is constrained to the output size so that zoom ratio
1193          * cannot exceed +/-4.0.
1194          */
1195         unsigned int min_width =
1196                 ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7;
1197         unsigned int min_height =
1198                 ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4;
1199         unsigned int max_width =
1200                 ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7;
1201         unsigned int max_height =
1202                 ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7;
1203
1204         crop->width = clamp_t(u32, crop->width, min_width, max_width);
1205         crop->height = clamp_t(u32, crop->height, min_height, max_height);
1206
1207         /* Crop can not go beyond of the input rectangle */
1208         crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH);
1209         crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH,
1210                               sink->width - crop->left);
1211         crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT);
1212         crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT,
1213                                sink->height - crop->top);
1214 }
1215
1216 /*
1217  * resizer_s_crop - handle set crop subdev operation
1218  * @sd : pointer to v4l2 subdev structure
1219  * @pad : subdev pad
1220  * @crop : pointer to crop structure
1221  * @which : active or try format
1222  * return -EINVAL or zero when succeed
1223  */
1224 static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
1225                           struct v4l2_subdev_crop *crop)
1226 {
1227         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1228         struct isp_device *isp = to_isp_device(res);
1229         struct v4l2_mbus_framefmt *format_sink, *format_source;
1230         struct resizer_ratio ratio;
1231
1232         /* Only sink pad has crop capability */
1233         if (crop->pad != RESZ_PAD_SINK)
1234                 return -EINVAL;
1235
1236         format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
1237                                            crop->which);
1238         format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
1239                                              crop->which);
1240
1241         dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
1242                 crop->rect.left, crop->rect.top, crop->rect.width,
1243                 crop->rect.height, crop->which);
1244
1245         dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
1246                 format_sink->width, format_sink->height,
1247                 format_source->width, format_source->height);
1248
1249         resizer_try_crop(format_sink, format_source, &crop->rect);
1250         *__resizer_get_crop(res, fh, crop->which) = crop->rect;
1251         resizer_calc_ratios(res, &crop->rect, format_source, &ratio);
1252
1253         if (crop->which == V4L2_SUBDEV_FORMAT_TRY)
1254                 return 0;
1255
1256         res->ratio = ratio;
1257         res->crop.active = crop->rect;
1258
1259         /*
1260          * s_crop can be called while streaming is on. In this case
1261          * the crop values will be set in the next IRQ.
1262          */
1263         if (res->state != ISP_PIPELINE_STREAM_STOPPED)
1264                 res->applycrop = 1;
1265
1266         return 0;
1267 }
1268
1269 /* resizer pixel formats */
1270 static const unsigned int resizer_formats[] = {
1271         V4L2_MBUS_FMT_UYVY8_1X16,
1272         V4L2_MBUS_FMT_YUYV8_1X16,
1273 };
1274
1275 static unsigned int resizer_max_in_width(struct isp_res_device *res)
1276 {
1277         struct isp_device *isp = to_isp_device(res);
1278
1279         if (res->input == RESIZER_INPUT_MEMORY) {
1280                 return MAX_IN_WIDTH_MEMORY_MODE;
1281         } else {
1282                 if (isp->revision == ISP_REVISION_1_0)
1283                         return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1;
1284                 else
1285                         return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2;
1286         }
1287 }
1288
1289 /*
1290  * resizer_try_format - Handle try format by pad subdev method
1291  * @res   : ISP resizer device
1292  * @fh    : V4L2 subdev file handle
1293  * @pad   : pad num
1294  * @fmt   : pointer to v4l2 format structure
1295  * @which : wanted subdev format
1296  */
1297 static void resizer_try_format(struct isp_res_device *res,
1298                                struct v4l2_subdev_fh *fh, unsigned int pad,
1299                                struct v4l2_mbus_framefmt *fmt,
1300                                enum v4l2_subdev_format_whence which)
1301 {
1302         struct v4l2_mbus_framefmt *format;
1303         struct resizer_ratio ratio;
1304         struct v4l2_rect crop;
1305
1306         switch (pad) {
1307         case RESZ_PAD_SINK:
1308                 if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 &&
1309                     fmt->code != V4L2_MBUS_FMT_UYVY8_1X16)
1310                         fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
1311
1312                 fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
1313                                      resizer_max_in_width(res));
1314                 fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
1315                                       MAX_IN_HEIGHT);
1316                 break;
1317
1318         case RESZ_PAD_SOURCE:
1319                 format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which);
1320                 fmt->code = format->code;
1321
1322                 crop = *__resizer_get_crop(res, fh, which);
1323                 resizer_calc_ratios(res, &crop, fmt, &ratio);
1324                 break;
1325         }
1326
1327         fmt->colorspace = V4L2_COLORSPACE_JPEG;
1328         fmt->field = V4L2_FIELD_NONE;
1329 }
1330
1331 /*
1332  * resizer_enum_mbus_code - Handle pixel format enumeration
1333  * @sd     : pointer to v4l2 subdev structure
1334  * @fh     : V4L2 subdev file handle
1335  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
1336  * return -EINVAL or zero on success
1337  */
1338 static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
1339                                   struct v4l2_subdev_fh *fh,
1340                                   struct v4l2_subdev_mbus_code_enum *code)
1341 {
1342         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1343         struct v4l2_mbus_framefmt *format;
1344
1345         if (code->pad == RESZ_PAD_SINK) {
1346                 if (code->index >= ARRAY_SIZE(resizer_formats))
1347                         return -EINVAL;
1348
1349                 code->code = resizer_formats[code->index];
1350         } else {
1351                 if (code->index != 0)
1352                         return -EINVAL;
1353
1354                 format = __resizer_get_format(res, fh, RESZ_PAD_SINK,
1355                                               V4L2_SUBDEV_FORMAT_TRY);
1356                 code->code = format->code;
1357         }
1358
1359         return 0;
1360 }
1361
1362 static int resizer_enum_frame_size(struct v4l2_subdev *sd,
1363                                    struct v4l2_subdev_fh *fh,
1364                                    struct v4l2_subdev_frame_size_enum *fse)
1365 {
1366         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1367         struct v4l2_mbus_framefmt format;
1368
1369         if (fse->index != 0)
1370                 return -EINVAL;
1371
1372         format.code = fse->code;
1373         format.width = 1;
1374         format.height = 1;
1375         resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
1376         fse->min_width = format.width;
1377         fse->min_height = format.height;
1378
1379         if (format.code != fse->code)
1380                 return -EINVAL;
1381
1382         format.code = fse->code;
1383         format.width = -1;
1384         format.height = -1;
1385         resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
1386         fse->max_width = format.width;
1387         fse->max_height = format.height;
1388
1389         return 0;
1390 }
1391
1392 /*
1393  * resizer_get_format - Handle get format by pads subdev method
1394  * @sd    : pointer to v4l2 subdev structure
1395  * @fh    : V4L2 subdev file handle
1396  * @fmt   : pointer to v4l2 subdev format structure
1397  * return -EINVAL or zero on sucess
1398  */
1399 static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
1400                               struct v4l2_subdev_format *fmt)
1401 {
1402         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1403         struct v4l2_mbus_framefmt *format;
1404
1405         format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
1406         if (format == NULL)
1407                 return -EINVAL;
1408
1409         fmt->format = *format;
1410         return 0;
1411 }
1412
1413 /*
1414  * resizer_set_format - Handle set format by pads subdev method
1415  * @sd    : pointer to v4l2 subdev structure
1416  * @fh    : V4L2 subdev file handle
1417  * @fmt   : pointer to v4l2 subdev format structure
1418  * return -EINVAL or zero on success
1419  */
1420 static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
1421                               struct v4l2_subdev_format *fmt)
1422 {
1423         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1424         struct v4l2_mbus_framefmt *format;
1425         struct v4l2_rect *crop;
1426
1427         format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
1428         if (format == NULL)
1429                 return -EINVAL;
1430
1431         resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which);
1432         *format = fmt->format;
1433
1434         if (fmt->pad == RESZ_PAD_SINK) {
1435                 /* reset crop rectangle */
1436                 crop = __resizer_get_crop(res, fh, fmt->which);
1437                 crop->left = 0;
1438                 crop->top = 0;
1439                 crop->width = fmt->format.width;
1440                 crop->height = fmt->format.height;
1441
1442                 /* Propagate the format from sink to source */
1443                 format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
1444                                               fmt->which);
1445                 *format = fmt->format;
1446                 resizer_try_format(res, fh, RESZ_PAD_SOURCE, format,
1447                                    fmt->which);
1448         }
1449
1450         if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
1451                 /* Compute and store the active crop rectangle and resizer
1452                  * ratios. format already points to the source pad active
1453                  * format.
1454                  */
1455                 res->crop.active = res->crop.request;
1456                 resizer_calc_ratios(res, &res->crop.active, format,
1457                                        &res->ratio);
1458         }
1459
1460         return 0;
1461 }
1462
1463 /*
1464  * resizer_init_formats - Initialize formats on all pads
1465  * @sd: ISP resizer V4L2 subdevice
1466  * @fh: V4L2 subdev file handle
1467  *
1468  * Initialize all pad formats with default values. If fh is not NULL, try
1469  * formats are initialized on the file handle. Otherwise active formats are
1470  * initialized on the device.
1471  */
1472 static int resizer_init_formats(struct v4l2_subdev *sd,
1473                                 struct v4l2_subdev_fh *fh)
1474 {
1475         struct v4l2_subdev_format format;
1476
1477         memset(&format, 0, sizeof(format));
1478         format.pad = RESZ_PAD_SINK;
1479         format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
1480         format.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
1481         format.format.width = 4096;
1482         format.format.height = 4096;
1483         resizer_set_format(sd, fh, &format);
1484
1485         return 0;
1486 }
1487
1488 /* subdev video operations */
1489 static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
1490         .s_stream = resizer_set_stream,
1491 };
1492
1493 /* subdev pad operations */
1494 static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
1495         .enum_mbus_code = resizer_enum_mbus_code,
1496         .enum_frame_size = resizer_enum_frame_size,
1497         .get_fmt = resizer_get_format,
1498         .set_fmt = resizer_set_format,
1499         .get_crop = resizer_g_crop,
1500         .set_crop = resizer_s_crop,
1501 };
1502
1503 /* subdev operations */
1504 static const struct v4l2_subdev_ops resizer_v4l2_ops = {
1505         .video = &resizer_v4l2_video_ops,
1506         .pad = &resizer_v4l2_pad_ops,
1507 };
1508
1509 /* subdev internal operations */
1510 static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
1511         .open = resizer_init_formats,
1512 };
1513
1514 /* -----------------------------------------------------------------------------
1515  * Media entity operations
1516  */
1517
1518 /*
1519  * resizer_link_setup - Setup resizer connections.
1520  * @entity : Pointer to media entity structure
1521  * @local  : Pointer to local pad array
1522  * @remote : Pointer to remote pad array
1523  * @flags  : Link flags
1524  * return -EINVAL or zero on success
1525  */
1526 static int resizer_link_setup(struct media_entity *entity,
1527                               const struct media_pad *local,
1528                               const struct media_pad *remote, u32 flags)
1529 {
1530         struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
1531         struct isp_res_device *res = v4l2_get_subdevdata(sd);
1532
1533         switch (local->index | media_entity_type(remote->entity)) {
1534         case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE:
1535                 /* read from memory */
1536                 if (flags & MEDIA_LNK_FL_ENABLED) {
1537                         if (res->input == RESIZER_INPUT_VP)
1538                                 return -EBUSY;
1539                         res->input = RESIZER_INPUT_MEMORY;
1540                 } else {
1541                         if (res->input == RESIZER_INPUT_MEMORY)
1542                                 res->input = RESIZER_INPUT_NONE;
1543                 }
1544                 break;
1545
1546         case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
1547                 /* read from ccdc or previewer */
1548                 if (flags & MEDIA_LNK_FL_ENABLED) {
1549                         if (res->input == RESIZER_INPUT_MEMORY)
1550                                 return -EBUSY;
1551                         res->input = RESIZER_INPUT_VP;
1552                 } else {
1553                         if (res->input == RESIZER_INPUT_VP)
1554                                 res->input = RESIZER_INPUT_NONE;
1555                 }
1556                 break;
1557
1558         case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
1559                 /* resizer always write to memory */
1560                 break;
1561
1562         default:
1563                 return -EINVAL;
1564         }
1565
1566         return 0;
1567 }
1568
1569 /* media operations */
1570 static const struct media_entity_operations resizer_media_ops = {
1571         .link_setup = resizer_link_setup,
1572 };
1573
1574 /*
1575  * resizer_init_entities - Initialize resizer subdev and media entity.
1576  * @res : Pointer to resizer device structure
1577  * return -ENOMEM or zero on success
1578  */
1579 static int resizer_init_entities(struct isp_res_device *res)
1580 {
1581         struct v4l2_subdev *sd = &res->subdev;
1582         struct media_pad *pads = res->pads;
1583         struct media_entity *me = &sd->entity;
1584         int ret;
1585
1586         res->input = RESIZER_INPUT_NONE;
1587
1588         v4l2_subdev_init(sd, &resizer_v4l2_ops);
1589         sd->internal_ops = &resizer_v4l2_internal_ops;
1590         strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name));
1591         sd->grp_id = 1 << 16;   /* group ID for isp subdevs */
1592         v4l2_set_subdevdata(sd, res);
1593         sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1594
1595         pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
1596         pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
1597
1598         me->ops = &resizer_media_ops;
1599         ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0);
1600         if (ret < 0)
1601                 return ret;
1602
1603         resizer_init_formats(sd, NULL);
1604
1605         res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
1606         res->video_in.ops = &resizer_video_ops;
1607         res->video_in.isp = to_isp_device(res);
1608         res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
1609         res->video_in.bpl_alignment = 32;
1610         res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1611         res->video_out.ops = &resizer_video_ops;
1612         res->video_out.isp = to_isp_device(res);
1613         res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
1614         res->video_out.bpl_alignment = 32;
1615
1616         ret = omap3isp_video_init(&res->video_in, "resizer");
1617         if (ret < 0)
1618                 return ret;
1619
1620         ret = omap3isp_video_init(&res->video_out, "resizer");
1621         if (ret < 0)
1622                 return ret;
1623
1624         /* Connect the video nodes to the resizer subdev. */
1625         ret = media_entity_create_link(&res->video_in.video.entity, 0,
1626                         &res->subdev.entity, RESZ_PAD_SINK, 0);
1627         if (ret < 0)
1628                 return ret;
1629
1630         ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
1631                         &res->video_out.video.entity, 0, 0);
1632         if (ret < 0)
1633                 return ret;
1634
1635         return 0;
1636 }
1637
1638 void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
1639 {
1640         media_entity_cleanup(&res->subdev.entity);
1641
1642         v4l2_device_unregister_subdev(&res->subdev);
1643         omap3isp_video_unregister(&res->video_in);
1644         omap3isp_video_unregister(&res->video_out);
1645 }
1646
1647 int omap3isp_resizer_register_entities(struct isp_res_device *res,
1648                                        struct v4l2_device *vdev)
1649 {
1650         int ret;
1651
1652         /* Register the subdev and video nodes. */
1653         ret = v4l2_device_register_subdev(vdev, &res->subdev);
1654         if (ret < 0)
1655                 goto error;
1656
1657         ret = omap3isp_video_register(&res->video_in, vdev);
1658         if (ret < 0)
1659                 goto error;
1660
1661         ret = omap3isp_video_register(&res->video_out, vdev);
1662         if (ret < 0)
1663                 goto error;
1664
1665         return 0;
1666
1667 error:
1668         omap3isp_resizer_unregister_entities(res);
1669         return ret;
1670 }
1671
1672 /* -----------------------------------------------------------------------------
1673  * ISP resizer initialization and cleanup
1674  */
1675
1676 void omap3isp_resizer_cleanup(struct isp_device *isp)
1677 {
1678 }
1679
1680 /*
1681  * isp_resizer_init - Resizer initialization.
1682  * @isp : Pointer to ISP device
1683  * return -ENOMEM or zero on success
1684  */
1685 int omap3isp_resizer_init(struct isp_device *isp)
1686 {
1687         struct isp_res_device *res = &isp->isp_res;
1688         int ret;
1689
1690         init_waitqueue_head(&res->wait);
1691         atomic_set(&res->stopping, 0);
1692         ret = resizer_init_entities(res);
1693         if (ret < 0)
1694                 goto out;
1695
1696 out:
1697         if (ret)
1698                 omap3isp_resizer_cleanup(isp);
1699
1700         return ret;
1701 }