diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/omap3isp/ispresizer.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/omap3isp/ispresizer.c')
-rw-r--r-- | drivers/media/video/omap3isp/ispresizer.c | 1738 |
1 files changed, 1738 insertions, 0 deletions
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c new file mode 100644 index 00000000000..0bb0f8cd36f --- /dev/null +++ b/drivers/media/video/omap3isp/ispresizer.c | |||
@@ -0,0 +1,1738 @@ | |||
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 | * The equations can't be easily reverted, as the >> 8 operation is not linear. | ||
718 | * In addition, not all input sizes can be achieved for a given output size. To | ||
719 | * get the highest input size lower than or equal to the requested input size, | ||
720 | * we need to compute the highest resizing ratio that satisfies the following | ||
721 | * inequality (taking the 4-tap mode width equation as an example) | ||
722 | * | ||
723 | * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7 | ||
724 | * | ||
725 | * (where iw is the requested input width) which can be rewritten as | ||
726 | * | ||
727 | * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 | ||
728 | * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b | ||
729 | * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16 | ||
730 | * | ||
731 | * where b is the value of the 8 least significant bits of the right hand side | ||
732 | * expression of the last inequality. The highest resizing ratio value will be | ||
733 | * achieved when b is equal to its maximum value of 255. That resizing ratio | ||
734 | * value will still satisfy the original inequality, as b will disappear when | ||
735 | * the expression will be shifted right by 8. | ||
736 | * | ||
737 | * The reverted the equations thus become | ||
738 | * | ||
739 | * - 8-phase, 4-tap mode | ||
740 | * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1) | ||
741 | * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1) | ||
742 | * - 4-phase, 7-tap mode | ||
743 | * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1) | ||
744 | * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1) | ||
745 | * | ||
746 | * The ratios are integer values, and are rounded down to ensure that the | ||
747 | * cropped input size is not bigger than the uncropped input size. | ||
748 | * | ||
749 | * As the number of phases/taps, used to select the correct equations to compute | ||
750 | * the ratio, depends on the ratio, we start with the 4-tap mode equations to | ||
751 | * compute an approximation of the ratio, and switch to the 7-tap mode equations | ||
752 | * if the approximation is higher than the ratio threshold. | ||
753 | * | ||
754 | * As the 7-tap mode equations will return a ratio smaller than or equal to the | ||
755 | * 4-tap mode equations, the resulting ratio could become lower than or equal to | ||
756 | * the ratio threshold. This 'equations loop' isn't an issue as long as the | ||
757 | * correct equations are used to compute the final input size. Starting with the | ||
758 | * 4-tap mode equations ensure that, in case of values resulting in a 'ratio | ||
759 | * loop', the smallest of the ratio values will be used, never exceeding the | ||
760 | * requested input size. | ||
761 | * | ||
762 | * We first clamp the output size according to the hardware capabilitie to avoid | ||
763 | * auto-cropping the input more than required to satisfy the TRM equations. The | ||
764 | * minimum output size is achieved with a scaling factor of 1024. It is thus | ||
765 | * computed using the 7-tap equations. | ||
766 | * | ||
767 | * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 | ||
768 | * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 | ||
769 | * | ||
770 | * Similarly, the maximum output size is achieved with a scaling factor of 64 | ||
771 | * and computed using the 4-tap equations. | ||
772 | * | ||
773 | * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 | ||
774 | * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 | ||
775 | * | ||
776 | * The additional +255 term compensates for the round down operation performed | ||
777 | * by the TRM equations when shifting the value right by 8 bits. | ||
778 | * | ||
779 | * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to | ||
780 | * the maximum value guarantees that the ratio value will never be smaller than | ||
781 | * the minimum, but it could still slightly exceed the maximum. Clamping the | ||
782 | * ratio will thus result in a resizing factor slightly larger than the | ||
783 | * requested value. | ||
784 | * | ||
785 | * To accommodate that, and make sure the TRM equations are satisfied exactly, we | ||
786 | * compute the input crop rectangle as the last step. | ||
787 | * | ||
788 | * As if the situation wasn't complex enough, the maximum output width depends | ||
789 | * on the vertical resizing ratio. Fortunately, the output height doesn't | ||
790 | * depend on the horizontal resizing ratio. We can then start by computing the | ||
791 | * output height and the vertical ratio, and then move to computing the output | ||
792 | * width and the horizontal ratio. | ||
793 | */ | ||
794 | static void resizer_calc_ratios(struct isp_res_device *res, | ||
795 | struct v4l2_rect *input, | ||
796 | struct v4l2_mbus_framefmt *output, | ||
797 | struct resizer_ratio *ratio) | ||
798 | { | ||
799 | struct isp_device *isp = to_isp_device(res); | ||
800 | const unsigned int spv = DEFAULT_PHASE; | ||
801 | const unsigned int sph = DEFAULT_PHASE; | ||
802 | unsigned int upscaled_width; | ||
803 | unsigned int upscaled_height; | ||
804 | unsigned int min_width; | ||
805 | unsigned int min_height; | ||
806 | unsigned int max_width; | ||
807 | unsigned int max_height; | ||
808 | unsigned int width_alignment; | ||
809 | unsigned int width; | ||
810 | unsigned int height; | ||
811 | |||
812 | /* | ||
813 | * Clamp the output height based on the hardware capabilities and | ||
814 | * compute the vertical resizing ratio. | ||
815 | */ | ||
816 | min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; | ||
817 | min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); | ||
818 | max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; | ||
819 | max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); | ||
820 | output->height = clamp(output->height, min_height, max_height); | ||
821 | |||
822 | ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) | ||
823 | / (output->height - 1); | ||
824 | if (ratio->vert > MID_RESIZE_VALUE) | ||
825 | ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv) | ||
826 | / (output->height - 1); | ||
827 | ratio->vert = clamp_t(unsigned int, ratio->vert, | ||
828 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
829 | |||
830 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
831 | upscaled_height = (output->height - 1) * ratio->vert | ||
832 | + 32 * spv + 16; | ||
833 | height = (upscaled_height >> 8) + 4; | ||
834 | } else { | ||
835 | upscaled_height = (output->height - 1) * ratio->vert | ||
836 | + 64 * spv + 32; | ||
837 | height = (upscaled_height >> 8) + 7; | ||
838 | } | ||
839 | |||
840 | /* | ||
841 | * Compute the minimum and maximum output widths based on the hardware | ||
842 | * capabilities. The maximum depends on the vertical resizing ratio. | ||
843 | */ | ||
844 | min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; | ||
845 | min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); | ||
846 | |||
847 | if (ratio->vert <= MID_RESIZE_VALUE) { | ||
848 | switch (isp->revision) { | ||
849 | case ISP_REVISION_1_0: | ||
850 | max_width = MAX_4TAP_OUT_WIDTH_ES1; | ||
851 | break; | ||
852 | |||
853 | case ISP_REVISION_2_0: | ||
854 | default: | ||
855 | max_width = MAX_4TAP_OUT_WIDTH_ES2; | ||
856 | break; | ||
857 | |||
858 | case ISP_REVISION_15_0: | ||
859 | max_width = MAX_4TAP_OUT_WIDTH_3630; | ||
860 | break; | ||
861 | } | ||
862 | } else { | ||
863 | switch (isp->revision) { | ||
864 | case ISP_REVISION_1_0: | ||
865 | max_width = MAX_7TAP_OUT_WIDTH_ES1; | ||
866 | break; | ||
867 | |||
868 | case ISP_REVISION_2_0: | ||
869 | default: | ||
870 | max_width = MAX_7TAP_OUT_WIDTH_ES2; | ||
871 | break; | ||
872 | |||
873 | case ISP_REVISION_15_0: | ||
874 | max_width = MAX_7TAP_OUT_WIDTH_3630; | ||
875 | break; | ||
876 | } | ||
877 | } | ||
878 | max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 | ||
879 | + 1, max_width); | ||
880 | |||
881 | /* | ||
882 | * The output width must be even, and must be a multiple of 16 bytes | ||
883 | * when upscaling vertically. Clamp the output width to the valid range. | ||
884 | * Take the alignment into account (the maximum width in 7-tap mode on | ||
885 | * ES2 isn't a multiple of 8) and align the result up to make sure it | ||
886 | * won't be smaller than the minimum. | ||
887 | */ | ||
888 | width_alignment = ratio->vert < 256 ? 8 : 2; | ||
889 | output->width = clamp(output->width, min_width, | ||
890 | max_width & ~(width_alignment - 1)); | ||
891 | output->width = ALIGN(output->width, width_alignment); | ||
892 | |||
893 | ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph) | ||
894 | / (output->width - 1); | ||
895 | if (ratio->horz > MID_RESIZE_VALUE) | ||
896 | ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph) | ||
897 | / (output->width - 1); | ||
898 | ratio->horz = clamp_t(unsigned int, ratio->horz, | ||
899 | MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); | ||
900 | |||
901 | if (ratio->horz <= MID_RESIZE_VALUE) { | ||
902 | upscaled_width = (output->width - 1) * ratio->horz | ||
903 | + 32 * sph + 16; | ||
904 | width = (upscaled_width >> 8) + 7; | ||
905 | } else { | ||
906 | upscaled_width = (output->width - 1) * ratio->horz | ||
907 | + 64 * sph + 32; | ||
908 | width = (upscaled_width >> 8) + 7; | ||
909 | } | ||
910 | |||
911 | /* Center the new crop rectangle. */ | ||
912 | input->left += (input->width - width) / 2; | ||
913 | input->top += (input->height - height) / 2; | ||
914 | input->width = width; | ||
915 | input->height = height; | ||
916 | } | ||
917 | |||
918 | /* | ||
919 | * resizer_set_crop_params - Setup hardware with cropping parameters | ||
920 | * @res : resizer private structure | ||
921 | * @crop_rect : current crop rectangle | ||
922 | * @ratio : resizer ratios | ||
923 | * return none | ||
924 | */ | ||
925 | static void resizer_set_crop_params(struct isp_res_device *res, | ||
926 | const struct v4l2_mbus_framefmt *input, | ||
927 | const struct v4l2_mbus_framefmt *output) | ||
928 | { | ||
929 | resizer_set_ratio(res, &res->ratio); | ||
930 | |||
931 | /* Set chrominance horizontal algorithm */ | ||
932 | if (res->ratio.horz >= RESIZE_DIVISOR) | ||
933 | resizer_set_bilinear(res, RSZ_THE_SAME); | ||
934 | else | ||
935 | resizer_set_bilinear(res, RSZ_BILINEAR); | ||
936 | |||
937 | resizer_adjust_bandwidth(res); | ||
938 | |||
939 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
940 | /* Calculate additional offset for crop */ | ||
941 | res->crop_offset = (res->crop.active.top * input->width + | ||
942 | res->crop.active.left) * 2; | ||
943 | /* | ||
944 | * Write lowest 4 bits of horizontal pixel offset (in pixels), | ||
945 | * vertical start must be 0. | ||
946 | */ | ||
947 | resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); | ||
948 | |||
949 | /* | ||
950 | * Set start (read) address for cropping, in bytes. | ||
951 | * Lowest 5 bits must be zero. | ||
952 | */ | ||
953 | __resizer_set_inaddr(res, | ||
954 | res->addr_base + (res->crop_offset & ~0x1f)); | ||
955 | } else { | ||
956 | /* | ||
957 | * Set vertical start line and horizontal starting pixel. | ||
958 | * If the input is from CCDC/PREV, horizontal start field is | ||
959 | * in bytes (twice number of pixels). | ||
960 | */ | ||
961 | resizer_set_start(res, res->crop.active.left * 2, | ||
962 | res->crop.active.top); | ||
963 | /* Input address and offset must be 0 for preview/ccdc input */ | ||
964 | __resizer_set_inaddr(res, 0); | ||
965 | resizer_set_input_offset(res, 0); | ||
966 | } | ||
967 | |||
968 | /* Set the input size */ | ||
969 | resizer_set_input_size(res, res->crop.active.width, | ||
970 | res->crop.active.height); | ||
971 | } | ||
972 | |||
973 | static void resizer_configure(struct isp_res_device *res) | ||
974 | { | ||
975 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
976 | struct resizer_luma_yenh luma = {0, 0, 0, 0}; | ||
977 | |||
978 | resizer_set_source(res, res->input); | ||
979 | |||
980 | informat = &res->formats[RESZ_PAD_SINK]; | ||
981 | outformat = &res->formats[RESZ_PAD_SOURCE]; | ||
982 | |||
983 | /* RESZ_PAD_SINK */ | ||
984 | if (res->input == RESIZER_INPUT_VP) | ||
985 | resizer_set_input_offset(res, 0); | ||
986 | else | ||
987 | resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); | ||
988 | |||
989 | /* YUV422 interleaved, default phase, no luma enhancement */ | ||
990 | resizer_set_intype(res, RSZ_YUV422); | ||
991 | resizer_set_ycpos(res, informat->code); | ||
992 | resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); | ||
993 | resizer_set_luma(res, &luma); | ||
994 | |||
995 | /* RESZ_PAD_SOURCE */ | ||
996 | resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); | ||
997 | resizer_set_output_size(res, outformat->width, outformat->height); | ||
998 | |||
999 | resizer_set_crop_params(res, informat, outformat); | ||
1000 | } | ||
1001 | |||
1002 | /* ----------------------------------------------------------------------------- | ||
1003 | * Interrupt handling | ||
1004 | */ | ||
1005 | |||
1006 | static void resizer_enable_oneshot(struct isp_res_device *res) | ||
1007 | { | ||
1008 | struct isp_device *isp = to_isp_device(res); | ||
1009 | |||
1010 | isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, | ||
1011 | ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); | ||
1012 | } | ||
1013 | |||
1014 | void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) | ||
1015 | { | ||
1016 | /* | ||
1017 | * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun | ||
1018 | * condition, the module was paused and now we have a buffer queued | ||
1019 | * on the output again. Restart the pipeline if running in continuous | ||
1020 | * mode. | ||
1021 | */ | ||
1022 | if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && | ||
1023 | res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1024 | resizer_enable_oneshot(res); | ||
1025 | isp_video_dmaqueue_flags_clr(&res->video_out); | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | static void resizer_isr_buffer(struct isp_res_device *res) | ||
1030 | { | ||
1031 | struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); | ||
1032 | struct isp_buffer *buffer; | ||
1033 | int restart = 0; | ||
1034 | |||
1035 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) | ||
1036 | return; | ||
1037 | |||
1038 | /* Complete the output buffer and, if reading from memory, the input | ||
1039 | * buffer. | ||
1040 | */ | ||
1041 | buffer = omap3isp_video_buffer_next(&res->video_out, res->error); | ||
1042 | if (buffer != NULL) { | ||
1043 | resizer_set_outaddr(res, buffer->isp_addr); | ||
1044 | restart = 1; | ||
1045 | } | ||
1046 | |||
1047 | pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; | ||
1048 | |||
1049 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1050 | buffer = omap3isp_video_buffer_next(&res->video_in, 0); | ||
1051 | if (buffer != NULL) | ||
1052 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1053 | pipe->state |= ISP_PIPELINE_IDLE_INPUT; | ||
1054 | } | ||
1055 | |||
1056 | if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { | ||
1057 | if (isp_pipeline_ready(pipe)) | ||
1058 | omap3isp_pipeline_set_stream(pipe, | ||
1059 | ISP_PIPELINE_STREAM_SINGLESHOT); | ||
1060 | } else { | ||
1061 | /* If an underrun occurs, the video queue operation handler will | ||
1062 | * restart the resizer. Otherwise restart it immediately. | ||
1063 | */ | ||
1064 | if (restart) | ||
1065 | resizer_enable_oneshot(res); | ||
1066 | } | ||
1067 | |||
1068 | res->error = 0; | ||
1069 | } | ||
1070 | |||
1071 | /* | ||
1072 | * omap3isp_resizer_isr - ISP resizer interrupt handler | ||
1073 | * | ||
1074 | * Manage the resizer video buffers and configure shadowed and busy-locked | ||
1075 | * registers. | ||
1076 | */ | ||
1077 | void omap3isp_resizer_isr(struct isp_res_device *res) | ||
1078 | { | ||
1079 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
1080 | |||
1081 | if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) | ||
1082 | return; | ||
1083 | |||
1084 | if (res->applycrop) { | ||
1085 | outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, | ||
1086 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1087 | informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, | ||
1088 | V4L2_SUBDEV_FORMAT_ACTIVE); | ||
1089 | resizer_set_crop_params(res, informat, outformat); | ||
1090 | res->applycrop = 0; | ||
1091 | } | ||
1092 | |||
1093 | resizer_isr_buffer(res); | ||
1094 | } | ||
1095 | |||
1096 | /* ----------------------------------------------------------------------------- | ||
1097 | * ISP video operations | ||
1098 | */ | ||
1099 | |||
1100 | static int resizer_video_queue(struct isp_video *video, | ||
1101 | struct isp_buffer *buffer) | ||
1102 | { | ||
1103 | struct isp_res_device *res = &video->isp->isp_res; | ||
1104 | |||
1105 | if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) | ||
1106 | resizer_set_inaddr(res, buffer->isp_addr); | ||
1107 | |||
1108 | /* | ||
1109 | * We now have a buffer queued on the output. Despite what the | ||
1110 | * TRM says, the resizer can't be restarted immediately. | ||
1111 | * Enabling it in one shot mode in the middle of a frame (or at | ||
1112 | * least asynchronously to the frame) results in the output | ||
1113 | * being shifted randomly left/right and up/down, as if the | ||
1114 | * hardware didn't synchronize itself to the beginning of the | ||
1115 | * frame correctly. | ||
1116 | * | ||
1117 | * Restart the resizer on the next sync interrupt if running in | ||
1118 | * continuous mode or when starting the stream. | ||
1119 | */ | ||
1120 | if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1121 | resizer_set_outaddr(res, buffer->isp_addr); | ||
1122 | |||
1123 | return 0; | ||
1124 | } | ||
1125 | |||
1126 | static const struct isp_video_operations resizer_video_ops = { | ||
1127 | .queue = resizer_video_queue, | ||
1128 | }; | ||
1129 | |||
1130 | /* ----------------------------------------------------------------------------- | ||
1131 | * V4L2 subdev operations | ||
1132 | */ | ||
1133 | |||
1134 | /* | ||
1135 | * resizer_set_stream - Enable/Disable streaming on resizer subdev | ||
1136 | * @sd: ISP resizer V4L2 subdev | ||
1137 | * @enable: 1 == Enable, 0 == Disable | ||
1138 | * | ||
1139 | * The resizer hardware can't be enabled without a memory buffer to write to. | ||
1140 | * As the s_stream operation is called in response to a STREAMON call without | ||
1141 | * any buffer queued yet, just update the state field and return immediately. | ||
1142 | * The resizer will be enabled in resizer_video_queue(). | ||
1143 | */ | ||
1144 | static int resizer_set_stream(struct v4l2_subdev *sd, int enable) | ||
1145 | { | ||
1146 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1147 | struct isp_video *video_out = &res->video_out; | ||
1148 | struct isp_device *isp = to_isp_device(res); | ||
1149 | struct device *dev = to_device(res); | ||
1150 | |||
1151 | if (res->state == ISP_PIPELINE_STREAM_STOPPED) { | ||
1152 | if (enable == ISP_PIPELINE_STREAM_STOPPED) | ||
1153 | return 0; | ||
1154 | |||
1155 | omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1156 | resizer_configure(res); | ||
1157 | res->error = 0; | ||
1158 | resizer_print_status(res); | ||
1159 | } | ||
1160 | |||
1161 | switch (enable) { | ||
1162 | case ISP_PIPELINE_STREAM_CONTINUOUS: | ||
1163 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1164 | if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { | ||
1165 | resizer_enable_oneshot(res); | ||
1166 | isp_video_dmaqueue_flags_clr(video_out); | ||
1167 | } | ||
1168 | break; | ||
1169 | |||
1170 | case ISP_PIPELINE_STREAM_SINGLESHOT: | ||
1171 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1172 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); | ||
1173 | omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1174 | |||
1175 | resizer_enable_oneshot(res); | ||
1176 | break; | ||
1177 | |||
1178 | case ISP_PIPELINE_STREAM_STOPPED: | ||
1179 | if (omap3isp_module_sync_idle(&sd->entity, &res->wait, | ||
1180 | &res->stopping)) | ||
1181 | dev_dbg(dev, "%s: module stop timeout.\n", sd->name); | ||
1182 | omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | | ||
1183 | OMAP3_ISP_SBL_RESIZER_WRITE); | ||
1184 | omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); | ||
1185 | isp_video_dmaqueue_flags_clr(video_out); | ||
1186 | break; | ||
1187 | } | ||
1188 | |||
1189 | res->state = enable; | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | /* | ||
1194 | * resizer_g_crop - handle get crop subdev operation | ||
1195 | * @sd : pointer to v4l2 subdev structure | ||
1196 | * @pad : subdev pad | ||
1197 | * @crop : pointer to crop structure | ||
1198 | * @which : active or try format | ||
1199 | * return zero | ||
1200 | */ | ||
1201 | static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1202 | struct v4l2_subdev_crop *crop) | ||
1203 | { | ||
1204 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1205 | struct v4l2_mbus_framefmt *format; | ||
1206 | struct resizer_ratio ratio; | ||
1207 | |||
1208 | /* Only sink pad has crop capability */ | ||
1209 | if (crop->pad != RESZ_PAD_SINK) | ||
1210 | return -EINVAL; | ||
1211 | |||
1212 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); | ||
1213 | crop->rect = *__resizer_get_crop(res, fh, crop->which); | ||
1214 | resizer_calc_ratios(res, &crop->rect, format, &ratio); | ||
1215 | |||
1216 | return 0; | ||
1217 | } | ||
1218 | |||
1219 | /* | ||
1220 | * resizer_try_crop - mangles crop parameters. | ||
1221 | */ | ||
1222 | static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, | ||
1223 | const struct v4l2_mbus_framefmt *source, | ||
1224 | struct v4l2_rect *crop) | ||
1225 | { | ||
1226 | const unsigned int spv = DEFAULT_PHASE; | ||
1227 | const unsigned int sph = DEFAULT_PHASE; | ||
1228 | |||
1229 | /* Crop rectangle is constrained to the output size so that zoom ratio | ||
1230 | * cannot exceed +/-4.0. | ||
1231 | */ | ||
1232 | unsigned int min_width = | ||
1233 | ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; | ||
1234 | unsigned int min_height = | ||
1235 | ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; | ||
1236 | unsigned int max_width = | ||
1237 | ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; | ||
1238 | unsigned int max_height = | ||
1239 | ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; | ||
1240 | |||
1241 | crop->width = clamp_t(u32, crop->width, min_width, max_width); | ||
1242 | crop->height = clamp_t(u32, crop->height, min_height, max_height); | ||
1243 | |||
1244 | /* Crop can not go beyond of the input rectangle */ | ||
1245 | crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); | ||
1246 | crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, | ||
1247 | sink->width - crop->left); | ||
1248 | crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); | ||
1249 | crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, | ||
1250 | sink->height - crop->top); | ||
1251 | } | ||
1252 | |||
1253 | /* | ||
1254 | * resizer_s_crop - handle set crop subdev operation | ||
1255 | * @sd : pointer to v4l2 subdev structure | ||
1256 | * @pad : subdev pad | ||
1257 | * @crop : pointer to crop structure | ||
1258 | * @which : active or try format | ||
1259 | * return -EINVAL or zero when succeed | ||
1260 | */ | ||
1261 | static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1262 | struct v4l2_subdev_crop *crop) | ||
1263 | { | ||
1264 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1265 | struct isp_device *isp = to_isp_device(res); | ||
1266 | struct v4l2_mbus_framefmt *format_sink, *format_source; | ||
1267 | struct resizer_ratio ratio; | ||
1268 | |||
1269 | /* Only sink pad has crop capability */ | ||
1270 | if (crop->pad != RESZ_PAD_SINK) | ||
1271 | return -EINVAL; | ||
1272 | |||
1273 | format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1274 | crop->which); | ||
1275 | format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1276 | crop->which); | ||
1277 | |||
1278 | dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, | ||
1279 | crop->rect.left, crop->rect.top, crop->rect.width, | ||
1280 | crop->rect.height, crop->which); | ||
1281 | |||
1282 | dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, | ||
1283 | format_sink->width, format_sink->height, | ||
1284 | format_source->width, format_source->height); | ||
1285 | |||
1286 | resizer_try_crop(format_sink, format_source, &crop->rect); | ||
1287 | *__resizer_get_crop(res, fh, crop->which) = crop->rect; | ||
1288 | resizer_calc_ratios(res, &crop->rect, format_source, &ratio); | ||
1289 | |||
1290 | if (crop->which == V4L2_SUBDEV_FORMAT_TRY) | ||
1291 | return 0; | ||
1292 | |||
1293 | res->ratio = ratio; | ||
1294 | res->crop.active = crop->rect; | ||
1295 | |||
1296 | /* | ||
1297 | * s_crop can be called while streaming is on. In this case | ||
1298 | * the crop values will be set in the next IRQ. | ||
1299 | */ | ||
1300 | if (res->state != ISP_PIPELINE_STREAM_STOPPED) | ||
1301 | res->applycrop = 1; | ||
1302 | |||
1303 | return 0; | ||
1304 | } | ||
1305 | |||
1306 | /* resizer pixel formats */ | ||
1307 | static const unsigned int resizer_formats[] = { | ||
1308 | V4L2_MBUS_FMT_UYVY8_1X16, | ||
1309 | V4L2_MBUS_FMT_YUYV8_1X16, | ||
1310 | }; | ||
1311 | |||
1312 | static unsigned int resizer_max_in_width(struct isp_res_device *res) | ||
1313 | { | ||
1314 | struct isp_device *isp = to_isp_device(res); | ||
1315 | |||
1316 | if (res->input == RESIZER_INPUT_MEMORY) { | ||
1317 | return MAX_IN_WIDTH_MEMORY_MODE; | ||
1318 | } else { | ||
1319 | if (isp->revision == ISP_REVISION_1_0) | ||
1320 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; | ||
1321 | else | ||
1322 | return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | /* | ||
1327 | * resizer_try_format - Handle try format by pad subdev method | ||
1328 | * @res : ISP resizer device | ||
1329 | * @fh : V4L2 subdev file handle | ||
1330 | * @pad : pad num | ||
1331 | * @fmt : pointer to v4l2 format structure | ||
1332 | * @which : wanted subdev format | ||
1333 | */ | ||
1334 | static void resizer_try_format(struct isp_res_device *res, | ||
1335 | struct v4l2_subdev_fh *fh, unsigned int pad, | ||
1336 | struct v4l2_mbus_framefmt *fmt, | ||
1337 | enum v4l2_subdev_format_whence which) | ||
1338 | { | ||
1339 | struct v4l2_mbus_framefmt *format; | ||
1340 | struct resizer_ratio ratio; | ||
1341 | struct v4l2_rect crop; | ||
1342 | |||
1343 | switch (pad) { | ||
1344 | case RESZ_PAD_SINK: | ||
1345 | if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && | ||
1346 | fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) | ||
1347 | fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1348 | |||
1349 | fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, | ||
1350 | resizer_max_in_width(res)); | ||
1351 | fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, | ||
1352 | MAX_IN_HEIGHT); | ||
1353 | break; | ||
1354 | |||
1355 | case RESZ_PAD_SOURCE: | ||
1356 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); | ||
1357 | fmt->code = format->code; | ||
1358 | |||
1359 | crop = *__resizer_get_crop(res, fh, which); | ||
1360 | resizer_calc_ratios(res, &crop, fmt, &ratio); | ||
1361 | break; | ||
1362 | } | ||
1363 | |||
1364 | fmt->colorspace = V4L2_COLORSPACE_JPEG; | ||
1365 | fmt->field = V4L2_FIELD_NONE; | ||
1366 | } | ||
1367 | |||
1368 | /* | ||
1369 | * resizer_enum_mbus_code - Handle pixel format enumeration | ||
1370 | * @sd : pointer to v4l2 subdev structure | ||
1371 | * @fh : V4L2 subdev file handle | ||
1372 | * @code : pointer to v4l2_subdev_mbus_code_enum structure | ||
1373 | * return -EINVAL or zero on success | ||
1374 | */ | ||
1375 | static int resizer_enum_mbus_code(struct v4l2_subdev *sd, | ||
1376 | struct v4l2_subdev_fh *fh, | ||
1377 | struct v4l2_subdev_mbus_code_enum *code) | ||
1378 | { | ||
1379 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1380 | struct v4l2_mbus_framefmt *format; | ||
1381 | |||
1382 | if (code->pad == RESZ_PAD_SINK) { | ||
1383 | if (code->index >= ARRAY_SIZE(resizer_formats)) | ||
1384 | return -EINVAL; | ||
1385 | |||
1386 | code->code = resizer_formats[code->index]; | ||
1387 | } else { | ||
1388 | if (code->index != 0) | ||
1389 | return -EINVAL; | ||
1390 | |||
1391 | format = __resizer_get_format(res, fh, RESZ_PAD_SINK, | ||
1392 | V4L2_SUBDEV_FORMAT_TRY); | ||
1393 | code->code = format->code; | ||
1394 | } | ||
1395 | |||
1396 | return 0; | ||
1397 | } | ||
1398 | |||
1399 | static int resizer_enum_frame_size(struct v4l2_subdev *sd, | ||
1400 | struct v4l2_subdev_fh *fh, | ||
1401 | struct v4l2_subdev_frame_size_enum *fse) | ||
1402 | { | ||
1403 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1404 | struct v4l2_mbus_framefmt format; | ||
1405 | |||
1406 | if (fse->index != 0) | ||
1407 | return -EINVAL; | ||
1408 | |||
1409 | format.code = fse->code; | ||
1410 | format.width = 1; | ||
1411 | format.height = 1; | ||
1412 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1413 | fse->min_width = format.width; | ||
1414 | fse->min_height = format.height; | ||
1415 | |||
1416 | if (format.code != fse->code) | ||
1417 | return -EINVAL; | ||
1418 | |||
1419 | format.code = fse->code; | ||
1420 | format.width = -1; | ||
1421 | format.height = -1; | ||
1422 | resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); | ||
1423 | fse->max_width = format.width; | ||
1424 | fse->max_height = format.height; | ||
1425 | |||
1426 | return 0; | ||
1427 | } | ||
1428 | |||
1429 | /* | ||
1430 | * resizer_get_format - Handle get format by pads subdev method | ||
1431 | * @sd : pointer to v4l2 subdev structure | ||
1432 | * @fh : V4L2 subdev file handle | ||
1433 | * @fmt : pointer to v4l2 subdev format structure | ||
1434 | * return -EINVAL or zero on success | ||
1435 | */ | ||
1436 | static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1437 | struct v4l2_subdev_format *fmt) | ||
1438 | { | ||
1439 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1440 | struct v4l2_mbus_framefmt *format; | ||
1441 | |||
1442 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1443 | if (format == NULL) | ||
1444 | return -EINVAL; | ||
1445 | |||
1446 | fmt->format = *format; | ||
1447 | return 0; | ||
1448 | } | ||
1449 | |||
1450 | /* | ||
1451 | * resizer_set_format - Handle set format by pads subdev method | ||
1452 | * @sd : pointer to v4l2 subdev structure | ||
1453 | * @fh : V4L2 subdev file handle | ||
1454 | * @fmt : pointer to v4l2 subdev format structure | ||
1455 | * return -EINVAL or zero on success | ||
1456 | */ | ||
1457 | static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1458 | struct v4l2_subdev_format *fmt) | ||
1459 | { | ||
1460 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1461 | struct v4l2_mbus_framefmt *format; | ||
1462 | struct v4l2_rect *crop; | ||
1463 | |||
1464 | format = __resizer_get_format(res, fh, fmt->pad, fmt->which); | ||
1465 | if (format == NULL) | ||
1466 | return -EINVAL; | ||
1467 | |||
1468 | resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); | ||
1469 | *format = fmt->format; | ||
1470 | |||
1471 | if (fmt->pad == RESZ_PAD_SINK) { | ||
1472 | /* reset crop rectangle */ | ||
1473 | crop = __resizer_get_crop(res, fh, fmt->which); | ||
1474 | crop->left = 0; | ||
1475 | crop->top = 0; | ||
1476 | crop->width = fmt->format.width; | ||
1477 | crop->height = fmt->format.height; | ||
1478 | |||
1479 | /* Propagate the format from sink to source */ | ||
1480 | format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, | ||
1481 | fmt->which); | ||
1482 | *format = fmt->format; | ||
1483 | resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, | ||
1484 | fmt->which); | ||
1485 | } | ||
1486 | |||
1487 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { | ||
1488 | /* Compute and store the active crop rectangle and resizer | ||
1489 | * ratios. format already points to the source pad active | ||
1490 | * format. | ||
1491 | */ | ||
1492 | res->crop.active = res->crop.request; | ||
1493 | resizer_calc_ratios(res, &res->crop.active, format, | ||
1494 | &res->ratio); | ||
1495 | } | ||
1496 | |||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | /* | ||
1501 | * resizer_init_formats - Initialize formats on all pads | ||
1502 | * @sd: ISP resizer V4L2 subdevice | ||
1503 | * @fh: V4L2 subdev file handle | ||
1504 | * | ||
1505 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1506 | * formats are initialized on the file handle. Otherwise active formats are | ||
1507 | * initialized on the device. | ||
1508 | */ | ||
1509 | static int resizer_init_formats(struct v4l2_subdev *sd, | ||
1510 | struct v4l2_subdev_fh *fh) | ||
1511 | { | ||
1512 | struct v4l2_subdev_format format; | ||
1513 | |||
1514 | memset(&format, 0, sizeof(format)); | ||
1515 | format.pad = RESZ_PAD_SINK; | ||
1516 | format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1517 | format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; | ||
1518 | format.format.width = 4096; | ||
1519 | format.format.height = 4096; | ||
1520 | resizer_set_format(sd, fh, &format); | ||
1521 | |||
1522 | return 0; | ||
1523 | } | ||
1524 | |||
1525 | /* subdev video operations */ | ||
1526 | static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { | ||
1527 | .s_stream = resizer_set_stream, | ||
1528 | }; | ||
1529 | |||
1530 | /* subdev pad operations */ | ||
1531 | static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { | ||
1532 | .enum_mbus_code = resizer_enum_mbus_code, | ||
1533 | .enum_frame_size = resizer_enum_frame_size, | ||
1534 | .get_fmt = resizer_get_format, | ||
1535 | .set_fmt = resizer_set_format, | ||
1536 | .get_crop = resizer_g_crop, | ||
1537 | .set_crop = resizer_s_crop, | ||
1538 | }; | ||
1539 | |||
1540 | /* subdev operations */ | ||
1541 | static const struct v4l2_subdev_ops resizer_v4l2_ops = { | ||
1542 | .video = &resizer_v4l2_video_ops, | ||
1543 | .pad = &resizer_v4l2_pad_ops, | ||
1544 | }; | ||
1545 | |||
1546 | /* subdev internal operations */ | ||
1547 | static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { | ||
1548 | .open = resizer_init_formats, | ||
1549 | }; | ||
1550 | |||
1551 | /* ----------------------------------------------------------------------------- | ||
1552 | * Media entity operations | ||
1553 | */ | ||
1554 | |||
1555 | /* | ||
1556 | * resizer_link_setup - Setup resizer connections. | ||
1557 | * @entity : Pointer to media entity structure | ||
1558 | * @local : Pointer to local pad array | ||
1559 | * @remote : Pointer to remote pad array | ||
1560 | * @flags : Link flags | ||
1561 | * return -EINVAL or zero on success | ||
1562 | */ | ||
1563 | static int resizer_link_setup(struct media_entity *entity, | ||
1564 | const struct media_pad *local, | ||
1565 | const struct media_pad *remote, u32 flags) | ||
1566 | { | ||
1567 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1568 | struct isp_res_device *res = v4l2_get_subdevdata(sd); | ||
1569 | |||
1570 | switch (local->index | media_entity_type(remote->entity)) { | ||
1571 | case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: | ||
1572 | /* read from memory */ | ||
1573 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1574 | if (res->input == RESIZER_INPUT_VP) | ||
1575 | return -EBUSY; | ||
1576 | res->input = RESIZER_INPUT_MEMORY; | ||
1577 | } else { | ||
1578 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1579 | res->input = RESIZER_INPUT_NONE; | ||
1580 | } | ||
1581 | break; | ||
1582 | |||
1583 | case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1584 | /* read from ccdc or previewer */ | ||
1585 | if (flags & MEDIA_LNK_FL_ENABLED) { | ||
1586 | if (res->input == RESIZER_INPUT_MEMORY) | ||
1587 | return -EBUSY; | ||
1588 | res->input = RESIZER_INPUT_VP; | ||
1589 | } else { | ||
1590 | if (res->input == RESIZER_INPUT_VP) | ||
1591 | res->input = RESIZER_INPUT_NONE; | ||
1592 | } | ||
1593 | break; | ||
1594 | |||
1595 | case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1596 | /* resizer always write to memory */ | ||
1597 | break; | ||
1598 | |||
1599 | default: | ||
1600 | return -EINVAL; | ||
1601 | } | ||
1602 | |||
1603 | return 0; | ||
1604 | } | ||
1605 | |||
1606 | /* media operations */ | ||
1607 | static const struct media_entity_operations resizer_media_ops = { | ||
1608 | .link_setup = resizer_link_setup, | ||
1609 | }; | ||
1610 | |||
1611 | /* | ||
1612 | * resizer_init_entities - Initialize resizer subdev and media entity. | ||
1613 | * @res : Pointer to resizer device structure | ||
1614 | * return -ENOMEM or zero on success | ||
1615 | */ | ||
1616 | static int resizer_init_entities(struct isp_res_device *res) | ||
1617 | { | ||
1618 | struct v4l2_subdev *sd = &res->subdev; | ||
1619 | struct media_pad *pads = res->pads; | ||
1620 | struct media_entity *me = &sd->entity; | ||
1621 | int ret; | ||
1622 | |||
1623 | res->input = RESIZER_INPUT_NONE; | ||
1624 | |||
1625 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1626 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1627 | strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); | ||
1628 | sd->grp_id = 1 << 16; /* group ID for isp subdevs */ | ||
1629 | v4l2_set_subdevdata(sd, res); | ||
1630 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1631 | |||
1632 | pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1633 | pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1634 | |||
1635 | me->ops = &resizer_media_ops; | ||
1636 | ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); | ||
1637 | if (ret < 0) | ||
1638 | return ret; | ||
1639 | |||
1640 | resizer_init_formats(sd, NULL); | ||
1641 | |||
1642 | res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
1643 | res->video_in.ops = &resizer_video_ops; | ||
1644 | res->video_in.isp = to_isp_device(res); | ||
1645 | res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1646 | res->video_in.bpl_alignment = 32; | ||
1647 | res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1648 | res->video_out.ops = &resizer_video_ops; | ||
1649 | res->video_out.isp = to_isp_device(res); | ||
1650 | res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; | ||
1651 | res->video_out.bpl_alignment = 32; | ||
1652 | |||
1653 | ret = omap3isp_video_init(&res->video_in, "resizer"); | ||
1654 | if (ret < 0) | ||
1655 | return ret; | ||
1656 | |||
1657 | ret = omap3isp_video_init(&res->video_out, "resizer"); | ||
1658 | if (ret < 0) | ||
1659 | return ret; | ||
1660 | |||
1661 | /* Connect the video nodes to the resizer subdev. */ | ||
1662 | ret = media_entity_create_link(&res->video_in.video.entity, 0, | ||
1663 | &res->subdev.entity, RESZ_PAD_SINK, 0); | ||
1664 | if (ret < 0) | ||
1665 | return ret; | ||
1666 | |||
1667 | ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, | ||
1668 | &res->video_out.video.entity, 0, 0); | ||
1669 | if (ret < 0) | ||
1670 | return ret; | ||
1671 | |||
1672 | return 0; | ||
1673 | } | ||
1674 | |||
1675 | void omap3isp_resizer_unregister_entities(struct isp_res_device *res) | ||
1676 | { | ||
1677 | media_entity_cleanup(&res->subdev.entity); | ||
1678 | |||
1679 | v4l2_device_unregister_subdev(&res->subdev); | ||
1680 | omap3isp_video_unregister(&res->video_in); | ||
1681 | omap3isp_video_unregister(&res->video_out); | ||
1682 | } | ||
1683 | |||
1684 | int omap3isp_resizer_register_entities(struct isp_res_device *res, | ||
1685 | struct v4l2_device *vdev) | ||
1686 | { | ||
1687 | int ret; | ||
1688 | |||
1689 | /* Register the subdev and video nodes. */ | ||
1690 | ret = v4l2_device_register_subdev(vdev, &res->subdev); | ||
1691 | if (ret < 0) | ||
1692 | goto error; | ||
1693 | |||
1694 | ret = omap3isp_video_register(&res->video_in, vdev); | ||
1695 | if (ret < 0) | ||
1696 | goto error; | ||
1697 | |||
1698 | ret = omap3isp_video_register(&res->video_out, vdev); | ||
1699 | if (ret < 0) | ||
1700 | goto error; | ||
1701 | |||
1702 | return 0; | ||
1703 | |||
1704 | error: | ||
1705 | omap3isp_resizer_unregister_entities(res); | ||
1706 | return ret; | ||
1707 | } | ||
1708 | |||
1709 | /* ----------------------------------------------------------------------------- | ||
1710 | * ISP resizer initialization and cleanup | ||
1711 | */ | ||
1712 | |||
1713 | void omap3isp_resizer_cleanup(struct isp_device *isp) | ||
1714 | { | ||
1715 | } | ||
1716 | |||
1717 | /* | ||
1718 | * isp_resizer_init - Resizer initialization. | ||
1719 | * @isp : Pointer to ISP device | ||
1720 | * return -ENOMEM or zero on success | ||
1721 | */ | ||
1722 | int omap3isp_resizer_init(struct isp_device *isp) | ||
1723 | { | ||
1724 | struct isp_res_device *res = &isp->isp_res; | ||
1725 | int ret; | ||
1726 | |||
1727 | init_waitqueue_head(&res->wait); | ||
1728 | atomic_set(&res->stopping, 0); | ||
1729 | ret = resizer_init_entities(res); | ||
1730 | if (ret < 0) | ||
1731 | goto out; | ||
1732 | |||
1733 | out: | ||
1734 | if (ret) | ||
1735 | omap3isp_resizer_cleanup(isp); | ||
1736 | |||
1737 | return ret; | ||
1738 | } | ||