diff options
author | Manjunath Hadli <manjunath.hadli@ti.com> | 2012-11-28 00:17:55 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-12-21 11:14:11 -0500 |
commit | 45e46b3bbe187a1bfc8f44c8d7ceaf851f0f4219 (patch) | |
tree | 211c78c2003abf97def64ff885ac7c9133d14bb5 | |
parent | 26c0e9992c266ad05278da72902a5f9724962c65 (diff) |
[media] davinci: vpfe: dm365: resizer driver based on media framework
Add the video resizer driver with the v4l2 media controller framework
which takes care of resizing the video frames with both up-scaling
downscaling facility. The driver supports both continuous and
single shot operations.The driver supports resizer as a subdevice
and a media entity. It has support for 2 resizers - resizerA
and resizerB.
Signed-off-by: Manjunath Hadli <manjunath.hadli@ti.com>
Signed-off-by: Lad, Prabhakar <prabhakar.lad@ti.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/staging/media/davinci_vpfe/dm365_resizer.c | 1999 | ||||
-rw-r--r-- | drivers/staging/media/davinci_vpfe/dm365_resizer.h | 244 |
2 files changed, 2243 insertions, 0 deletions
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c new file mode 100644 index 000000000000..9cb0262b9b33 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c | |||
@@ -0,0 +1,1999 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments Inc | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | * | ||
17 | * Contributors: | ||
18 | * Manjunath Hadli <manjunath.hadli@ti.com> | ||
19 | * Prabhakar Lad <prabhakar.lad@ti.com> | ||
20 | * | ||
21 | * | ||
22 | * Resizer allows upscaling or downscaling a image to a desired | ||
23 | * resolution. There are 2 resizer modules. both operating on the | ||
24 | * same input image, but can have different output resolution. | ||
25 | */ | ||
26 | |||
27 | #include "dm365_ipipe_hw.h" | ||
28 | #include "dm365_resizer.h" | ||
29 | |||
30 | #define MIN_IN_WIDTH 32 | ||
31 | #define MIN_IN_HEIGHT 32 | ||
32 | #define MAX_IN_WIDTH 4095 | ||
33 | #define MAX_IN_HEIGHT 4095 | ||
34 | #define MIN_OUT_WIDTH 16 | ||
35 | #define MIN_OUT_HEIGHT 2 | ||
36 | |||
37 | static const unsigned int resizer_input_formats[] = { | ||
38 | V4L2_MBUS_FMT_UYVY8_2X8, | ||
39 | V4L2_MBUS_FMT_Y8_1X8, | ||
40 | V4L2_MBUS_FMT_UV8_1X8, | ||
41 | V4L2_MBUS_FMT_SGRBG12_1X12, | ||
42 | }; | ||
43 | |||
44 | static const unsigned int resizer_output_formats[] = { | ||
45 | V4L2_MBUS_FMT_UYVY8_2X8, | ||
46 | V4L2_MBUS_FMT_Y8_1X8, | ||
47 | V4L2_MBUS_FMT_UV8_1X8, | ||
48 | V4L2_MBUS_FMT_YDYUYDYV8_1X16, | ||
49 | V4L2_MBUS_FMT_SGRBG12_1X12, | ||
50 | }; | ||
51 | |||
52 | /* resizer_calculate_line_length() - This function calculates the line length of | ||
53 | * various image planes at the input and | ||
54 | * output. | ||
55 | */ | ||
56 | static void | ||
57 | resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width, | ||
58 | int height, int *line_len, int *line_len_c) | ||
59 | { | ||
60 | *line_len = 0; | ||
61 | *line_len_c = 0; | ||
62 | |||
63 | if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || | ||
64 | pix == V4L2_MBUS_FMT_SGRBG12_1X12) { | ||
65 | *line_len = width << 1; | ||
66 | } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || | ||
67 | pix == V4L2_MBUS_FMT_UV8_1X8) { | ||
68 | *line_len = width; | ||
69 | *line_len_c = width; | ||
70 | } else { | ||
71 | /* YUV 420 */ | ||
72 | /* round width to upper 32 byte boundary */ | ||
73 | *line_len = width; | ||
74 | *line_len_c = width; | ||
75 | } | ||
76 | /* adjust the line len to be a multiple of 32 */ | ||
77 | *line_len += 31; | ||
78 | *line_len &= ~0x1f; | ||
79 | *line_len_c += 31; | ||
80 | *line_len_c &= ~0x1f; | ||
81 | } | ||
82 | |||
83 | static inline int | ||
84 | resizer_validate_output_image_format(struct device *dev, | ||
85 | struct v4l2_mbus_framefmt *format, | ||
86 | int *in_line_len, int *in_line_len_c) | ||
87 | { | ||
88 | if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 && | ||
89 | format->code != V4L2_MBUS_FMT_Y8_1X8 && | ||
90 | format->code != V4L2_MBUS_FMT_UV8_1X8 && | ||
91 | format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 && | ||
92 | format->code != V4L2_MBUS_FMT_SGRBG12_1X12) { | ||
93 | dev_err(dev, "Invalid Mbus format, %d\n", format->code); | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | if (!format->width || !format->height) { | ||
97 | dev_err(dev, "invalid width or height\n"); | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | resizer_calculate_line_length(format->code, format->width, | ||
101 | format->height, in_line_len, in_line_len_c); | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass) | ||
107 | { | ||
108 | struct resizer_params *param = &resizer->config; | ||
109 | |||
110 | param->rsz_rsc_param[RSZ_A].cen = DISABLE; | ||
111 | param->rsz_rsc_param[RSZ_A].yen = DISABLE; | ||
112 | param->rsz_rsc_param[RSZ_A].v_phs_y = 0; | ||
113 | param->rsz_rsc_param[RSZ_A].v_phs_c = 0; | ||
114 | param->rsz_rsc_param[RSZ_A].v_dif = 256; | ||
115 | param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; | ||
116 | param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; | ||
117 | param->rsz_rsc_param[RSZ_A].h_phs = 0; | ||
118 | param->rsz_rsc_param[RSZ_A].h_dif = 256; | ||
119 | param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; | ||
120 | param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; | ||
121 | param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; | ||
122 | param->rsz2rgb[RSZ_A].rgb_en = DISABLE; | ||
123 | param->rsz_en[RSZ_A] = ENABLE; | ||
124 | param->rsz_en[RSZ_B] = DISABLE; | ||
125 | if (bypass) { | ||
126 | param->rsz_rsc_param[RSZ_A].i_vps = 0; | ||
127 | param->rsz_rsc_param[RSZ_A].i_hps = 0; | ||
128 | /* Raw Bypass */ | ||
129 | param->rsz_common.passthrough = BYPASS_ON; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | static void | ||
134 | configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, | ||
135 | void *output_spec, unsigned char partial, | ||
136 | unsigned flag) | ||
137 | { | ||
138 | struct resizer_params *param = &resizer->config; | ||
139 | struct v4l2_mbus_framefmt *outformat; | ||
140 | struct vpfe_rsz_output_spec *output; | ||
141 | |||
142 | if (index == RSZ_A && | ||
143 | resizer->resizer_a.output == RESIZER_OUTPUT_NONE) { | ||
144 | param->rsz_en[index] = DISABLE; | ||
145 | return; | ||
146 | } | ||
147 | if (index == RSZ_B && | ||
148 | resizer->resizer_b.output == RESIZER_OUTPUT_NONE) { | ||
149 | param->rsz_en[index] = DISABLE; | ||
150 | return; | ||
151 | } | ||
152 | output = (struct vpfe_rsz_output_spec *)output_spec; | ||
153 | param->rsz_en[index] = ENABLE; | ||
154 | if (partial) { | ||
155 | param->rsz_rsc_param[index].h_flip = output->h_flip; | ||
156 | param->rsz_rsc_param[index].v_flip = output->v_flip; | ||
157 | param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; | ||
158 | param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; | ||
159 | param->rsz_rsc_param[index].v_lpf_int_y = | ||
160 | output->v_lpf_int_y; | ||
161 | param->rsz_rsc_param[index].v_lpf_int_c = | ||
162 | output->v_lpf_int_c; | ||
163 | param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; | ||
164 | param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; | ||
165 | param->rsz_rsc_param[index].h_lpf_int_y = | ||
166 | output->h_lpf_int_y; | ||
167 | param->rsz_rsc_param[index].h_lpf_int_c = | ||
168 | output->h_lpf_int_c; | ||
169 | param->rsz_rsc_param[index].dscale_en = | ||
170 | output->en_down_scale; | ||
171 | param->rsz_rsc_param[index].h_dscale_ave_sz = | ||
172 | output->h_dscale_ave_sz; | ||
173 | param->rsz_rsc_param[index].v_dscale_ave_sz = | ||
174 | output->v_dscale_ave_sz; | ||
175 | param->ext_mem_param[index].user_y_ofst = | ||
176 | (output->user_y_ofst + 31) & ~0x1f; | ||
177 | param->ext_mem_param[index].user_c_ofst = | ||
178 | (output->user_c_ofst + 31) & ~0x1f; | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | if (index == RSZ_A) | ||
183 | outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; | ||
184 | else | ||
185 | outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; | ||
186 | param->rsz_rsc_param[index].o_vsz = outformat->height - 1; | ||
187 | param->rsz_rsc_param[index].o_hsz = outformat->width - 1; | ||
188 | param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y; | ||
189 | param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height; | ||
190 | param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c; | ||
191 | param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height; | ||
192 | |||
193 | if (!flag) | ||
194 | return; | ||
195 | /* update common parameters */ | ||
196 | param->rsz_rsc_param[index].h_flip = output->h_flip; | ||
197 | param->rsz_rsc_param[index].v_flip = output->v_flip; | ||
198 | param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; | ||
199 | param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; | ||
200 | param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; | ||
201 | param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; | ||
202 | param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; | ||
203 | param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; | ||
204 | param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; | ||
205 | param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; | ||
206 | param->rsz_rsc_param[index].dscale_en = output->en_down_scale; | ||
207 | param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; | ||
208 | param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; | ||
209 | param->ext_mem_param[index].user_y_ofst = | ||
210 | (output->user_y_ofst + 31) & ~0x1f; | ||
211 | param->ext_mem_param[index].user_c_ofst = | ||
212 | (output->user_c_ofst + 31) & ~0x1f; | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer | ||
217 | * A or B. This is called after setting | ||
218 | * the input size or output size. | ||
219 | * @resizer: Pointer to VPFE resizer subdevice. | ||
220 | * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. | ||
221 | */ | ||
222 | void | ||
223 | resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) | ||
224 | { | ||
225 | struct resizer_params *param = &resizer->config; | ||
226 | struct v4l2_mbus_framefmt *informat, *outformat; | ||
227 | |||
228 | informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; | ||
229 | |||
230 | if (index == RSZ_A) | ||
231 | outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; | ||
232 | else | ||
233 | outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; | ||
234 | |||
235 | if (outformat->field != V4L2_FIELD_INTERLACED) | ||
236 | param->rsz_rsc_param[index].v_dif = | ||
237 | ((informat->height) * 256) / (outformat->height); | ||
238 | else | ||
239 | param->rsz_rsc_param[index].v_dif = | ||
240 | ((informat->height >> 1) * 256) / (outformat->height); | ||
241 | param->rsz_rsc_param[index].h_dif = | ||
242 | ((informat->width) * 256) / (outformat->width); | ||
243 | } | ||
244 | |||
245 | void | ||
246 | static resizer_enable_422_420_conversion(struct resizer_params *param, | ||
247 | int index, bool en) | ||
248 | { | ||
249 | param->rsz_rsc_param[index].cen = en; | ||
250 | param->rsz_rsc_param[index].yen = en; | ||
251 | } | ||
252 | |||
253 | /* resizer_calculate_sdram_offsets() - This function calculates the offsets from | ||
254 | * start of buffer for the C plane when | ||
255 | * output format is YUV420SP. It also | ||
256 | * calculates the offsets from the start of | ||
257 | * the buffer when the image is flipped | ||
258 | * vertically or horizontally for ycbcr/y/c | ||
259 | * planes. | ||
260 | * @resizer: Pointer to resizer subdevice. | ||
261 | * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. | ||
262 | */ | ||
263 | static int | ||
264 | resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) | ||
265 | { | ||
266 | struct resizer_params *param = &resizer->config; | ||
267 | struct v4l2_mbus_framefmt *outformat; | ||
268 | int bytesperpixel = 2; | ||
269 | int image_height; | ||
270 | int image_width; | ||
271 | int yuv_420 = 0; | ||
272 | int offset = 0; | ||
273 | |||
274 | if (index == RSZ_A) | ||
275 | outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; | ||
276 | else | ||
277 | outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; | ||
278 | |||
279 | image_height = outformat->height + 1; | ||
280 | image_width = outformat->width + 1; | ||
281 | param->ext_mem_param[index].c_offset = 0; | ||
282 | param->ext_mem_param[index].flip_ofst_y = 0; | ||
283 | param->ext_mem_param[index].flip_ofst_c = 0; | ||
284 | if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) { | ||
285 | /* YUV 420 */ | ||
286 | yuv_420 = 1; | ||
287 | bytesperpixel = 1; | ||
288 | } | ||
289 | |||
290 | if (param->rsz_rsc_param[index].h_flip) | ||
291 | /* width * bytesperpixel - 1 */ | ||
292 | offset = (image_width * bytesperpixel) - 1; | ||
293 | if (param->rsz_rsc_param[index].v_flip) | ||
294 | offset += (image_height - 1) * | ||
295 | param->ext_mem_param[index].rsz_sdr_oft_y; | ||
296 | param->ext_mem_param[index].flip_ofst_y = offset; | ||
297 | if (!yuv_420) | ||
298 | return 0; | ||
299 | offset = 0; | ||
300 | /* half height for c-plane */ | ||
301 | if (param->rsz_rsc_param[index].h_flip) | ||
302 | /* width * bytesperpixel - 1 */ | ||
303 | offset = image_width - 1; | ||
304 | if (param->rsz_rsc_param[index].v_flip) | ||
305 | offset += (((image_height >> 1) - 1) * | ||
306 | param->ext_mem_param[index].rsz_sdr_oft_c); | ||
307 | param->ext_mem_param[index].flip_ofst_c = offset; | ||
308 | param->ext_mem_param[index].c_offset = | ||
309 | param->ext_mem_param[index].rsz_sdr_oft_y * image_height; | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | int resizer_configure_output_win(struct vpfe_resizer_device *resizer) | ||
314 | { | ||
315 | struct resizer_params *param = &resizer->config; | ||
316 | struct vpfe_rsz_output_spec output_specs; | ||
317 | struct v4l2_mbus_framefmt *outformat; | ||
318 | int line_len_c; | ||
319 | int line_len; | ||
320 | int ret; | ||
321 | |||
322 | outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; | ||
323 | |||
324 | output_specs.vst_y = param->user_config.vst; | ||
325 | if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) | ||
326 | output_specs.vst_c = param->user_config.vst; | ||
327 | |||
328 | configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0); | ||
329 | resizer_calculate_line_length(outformat->code, | ||
330 | param->rsz_rsc_param[0].o_hsz + 1, | ||
331 | param->rsz_rsc_param[0].o_vsz + 1, | ||
332 | &line_len, &line_len_c); | ||
333 | param->ext_mem_param[0].rsz_sdr_oft_y = line_len; | ||
334 | param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; | ||
335 | resizer_calculate_resize_ratios(resizer, RSZ_A); | ||
336 | if (param->rsz_en[RSZ_B]) | ||
337 | resizer_calculate_resize_ratios(resizer, RSZ_B); | ||
338 | |||
339 | if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) | ||
340 | resizer_enable_422_420_conversion(param, RSZ_A, ENABLE); | ||
341 | else | ||
342 | resizer_enable_422_420_conversion(param, RSZ_A, DISABLE); | ||
343 | |||
344 | ret = resizer_calculate_sdram_offsets(resizer, RSZ_A); | ||
345 | if (!ret && param->rsz_en[RSZ_B]) | ||
346 | ret = resizer_calculate_sdram_offsets(resizer, RSZ_B); | ||
347 | |||
348 | if (ret) | ||
349 | pr_err("Error in calculating sdram offsets\n"); | ||
350 | return ret; | ||
351 | } | ||
352 | |||
353 | static int | ||
354 | resizer_calculate_down_scale_f_div_param(struct device *dev, | ||
355 | int input_width, int output_width, | ||
356 | struct resizer_scale_param *param) | ||
357 | { | ||
358 | /* rsz = R, input_width = H, output width = h in the equation */ | ||
359 | unsigned int two_power; | ||
360 | unsigned int upper_h1; | ||
361 | unsigned int upper_h2; | ||
362 | unsigned int val1; | ||
363 | unsigned int val; | ||
364 | unsigned int rsz; | ||
365 | unsigned int h1; | ||
366 | unsigned int h2; | ||
367 | unsigned int o; | ||
368 | unsigned int n; | ||
369 | |||
370 | upper_h1 = input_width >> 1; | ||
371 | n = param->h_dscale_ave_sz; | ||
372 | /* 2 ^ (scale+1) */ | ||
373 | two_power = 1 << (n + 1); | ||
374 | upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); | ||
375 | upper_h2 = input_width - upper_h1; | ||
376 | if (upper_h2 % two_power) { | ||
377 | dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | two_power = 1 << n; | ||
381 | rsz = (input_width << 8) / output_width; | ||
382 | val = rsz * two_power; | ||
383 | val = ((upper_h1 << 8) / val) + 1; | ||
384 | if (!(val % 2)) { | ||
385 | h1 = val; | ||
386 | } else { | ||
387 | val = upper_h1 << 8; | ||
388 | val >>= n + 1; | ||
389 | val -= rsz >> 1; | ||
390 | val /= rsz << 1; | ||
391 | val <<= 1; | ||
392 | val += 2; | ||
393 | h1 = val; | ||
394 | } | ||
395 | o = 10 + (two_power << 2); | ||
396 | if (((input_width << 7) / rsz) % 2) | ||
397 | o += (((CEIL(rsz, 1024)) << 1) << n); | ||
398 | h2 = output_width - h1; | ||
399 | /* phi */ | ||
400 | val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); | ||
401 | /* skip */ | ||
402 | val1 = ((val - 1024) >> 9) << 1; | ||
403 | param->f_div.num_passes = MAX_PASSES; | ||
404 | param->f_div.pass[0].o_hsz = h1 - 1; | ||
405 | param->f_div.pass[0].i_hps = 0; | ||
406 | param->f_div.pass[0].h_phs = 0; | ||
407 | param->f_div.pass[0].src_hps = 0; | ||
408 | param->f_div.pass[0].src_hsz = upper_h1 + o; | ||
409 | param->f_div.pass[1].o_hsz = h2 - 1; | ||
410 | param->f_div.pass[1].i_hps = 10 + (val1 * two_power); | ||
411 | param->f_div.pass[1].h_phs = (val - (val1 << 8)); | ||
412 | param->f_div.pass[1].src_hps = upper_h1 - o; | ||
413 | param->f_div.pass[1].src_hsz = upper_h2 + o; | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int | ||
419 | resizer_configure_common_in_params(struct vpfe_resizer_device *resizer) | ||
420 | { | ||
421 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
422 | struct resizer_params *param = &resizer->config; | ||
423 | struct vpfe_rsz_config_params *user_config; | ||
424 | struct v4l2_mbus_framefmt *informat; | ||
425 | |||
426 | informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; | ||
427 | user_config = &resizer->config.user_config; | ||
428 | param->rsz_common.vps = param->user_config.vst; | ||
429 | param->rsz_common.hps = param->user_config.hst; | ||
430 | |||
431 | if (vpfe_ipipeif_decimation_enabled(vpfe_dev)) | ||
432 | param->rsz_common.hsz = (((informat->width - 1) * | ||
433 | IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev)); | ||
434 | else | ||
435 | param->rsz_common.hsz = informat->width - 1; | ||
436 | |||
437 | if (informat->field == V4L2_FIELD_INTERLACED) | ||
438 | param->rsz_common.vsz = (informat->height - 1) >> 1; | ||
439 | else | ||
440 | param->rsz_common.vsz = informat->height - 1; | ||
441 | |||
442 | param->rsz_common.raw_flip = 0; | ||
443 | |||
444 | if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF) | ||
445 | param->rsz_common.source = IPIPEIF_DATA; | ||
446 | else | ||
447 | param->rsz_common.source = IPIPE_DATA; | ||
448 | |||
449 | switch (informat->code) { | ||
450 | case V4L2_MBUS_FMT_UYVY8_2X8: | ||
451 | param->rsz_common.src_img_fmt = RSZ_IMG_422; | ||
452 | param->rsz_common.raw_flip = 0; | ||
453 | break; | ||
454 | |||
455 | case V4L2_MBUS_FMT_Y8_1X8: | ||
456 | param->rsz_common.src_img_fmt = RSZ_IMG_420; | ||
457 | /* Select y */ | ||
458 | param->rsz_common.y_c = 0; | ||
459 | param->rsz_common.raw_flip = 0; | ||
460 | break; | ||
461 | |||
462 | case V4L2_MBUS_FMT_UV8_1X8: | ||
463 | param->rsz_common.src_img_fmt = RSZ_IMG_420; | ||
464 | /* Select y */ | ||
465 | param->rsz_common.y_c = 1; | ||
466 | param->rsz_common.raw_flip = 0; | ||
467 | break; | ||
468 | |||
469 | case V4L2_MBUS_FMT_SGRBG12_1X12: | ||
470 | param->rsz_common.raw_flip = 1; | ||
471 | break; | ||
472 | |||
473 | default: | ||
474 | param->rsz_common.src_img_fmt = RSZ_IMG_422; | ||
475 | param->rsz_common.source = IPIPE_DATA; | ||
476 | } | ||
477 | |||
478 | param->rsz_common.yuv_y_min = user_config->yuv_y_min; | ||
479 | param->rsz_common.yuv_y_max = user_config->yuv_y_max; | ||
480 | param->rsz_common.yuv_c_min = user_config->yuv_c_min; | ||
481 | param->rsz_common.yuv_c_max = user_config->yuv_c_max; | ||
482 | param->rsz_common.out_chr_pos = user_config->out_chr_pos; | ||
483 | param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even; | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | static int | ||
488 | resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer) | ||
489 | { | ||
490 | struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; | ||
491 | struct resizer_params *param = &resizer->config; | ||
492 | struct vpfe_rsz_config_params *cont_config; | ||
493 | int line_len_c; | ||
494 | int line_len; | ||
495 | int ret; | ||
496 | |||
497 | if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) { | ||
498 | dev_err(dev, "enable resizer - Resizer-A\n"); | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | |||
502 | cont_config = &resizer->config.user_config; | ||
503 | param->rsz_en[RSZ_A] = ENABLE; | ||
504 | configure_resizer_out_params(resizer, RSZ_A, | ||
505 | &cont_config->output1, 1, 0); | ||
506 | param->rsz_en[RSZ_B] = DISABLE; | ||
507 | param->oper_mode = RESIZER_MODE_CONTINIOUS; | ||
508 | |||
509 | if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { | ||
510 | struct v4l2_mbus_framefmt *outformat2; | ||
511 | |||
512 | param->rsz_en[RSZ_B] = ENABLE; | ||
513 | outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; | ||
514 | ret = resizer_validate_output_image_format(dev, outformat2, | ||
515 | &line_len, &line_len_c); | ||
516 | if (ret) | ||
517 | return ret; | ||
518 | param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; | ||
519 | param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; | ||
520 | configure_resizer_out_params(resizer, RSZ_B, | ||
521 | &cont_config->output2, 0, 1); | ||
522 | if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) | ||
523 | resizer_enable_422_420_conversion(param, | ||
524 | RSZ_B, ENABLE); | ||
525 | else | ||
526 | resizer_enable_422_420_conversion(param, | ||
527 | RSZ_B, DISABLE); | ||
528 | } | ||
529 | resizer_configure_common_in_params(resizer); | ||
530 | ret = resizer_configure_output_win(resizer); | ||
531 | if (ret) | ||
532 | return ret; | ||
533 | |||
534 | param->rsz_common.passthrough = cont_config->bypass; | ||
535 | if (cont_config->bypass) | ||
536 | resizer_configure_passthru(resizer, 1); | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static inline int | ||
542 | resizer_validate_input_image_format(struct device *dev, | ||
543 | enum v4l2_mbus_pixelcode pix, | ||
544 | int width, int height, int *line_len) | ||
545 | { | ||
546 | int val; | ||
547 | |||
548 | if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && | ||
549 | pix != V4L2_MBUS_FMT_Y8_1X8 && | ||
550 | pix != V4L2_MBUS_FMT_UV8_1X8 && | ||
551 | pix != V4L2_MBUS_FMT_SGRBG12_1X12) { | ||
552 | dev_err(dev, | ||
553 | "resizer validate output: pix format not supported, %d\n", pix); | ||
554 | return -EINVAL; | ||
555 | } | ||
556 | |||
557 | if (!width || !height) { | ||
558 | dev_err(dev, | ||
559 | "resizer validate input: invalid width or height\n"); | ||
560 | return -EINVAL; | ||
561 | } | ||
562 | |||
563 | if (pix == V4L2_MBUS_FMT_UV8_1X8) | ||
564 | resizer_calculate_line_length(pix, width, | ||
565 | height, &val, line_len); | ||
566 | else | ||
567 | resizer_calculate_line_length(pix, width, | ||
568 | height, line_len, &val); | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static int | ||
574 | resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en, | ||
575 | unsigned char rsz, unsigned char frame_div_mode_en, | ||
576 | int width) | ||
577 | { | ||
578 | if (dec_en && frame_div_mode_en) { | ||
579 | dev_err(dev, | ||
580 | "dec_en & frame_div_mode_en can not enabled simultaneously\n"); | ||
581 | return -EINVAL; | ||
582 | } | ||
583 | |||
584 | if (frame_div_mode_en) { | ||
585 | dev_err(dev, "frame_div_mode mode not supported\n"); | ||
586 | return -EINVAL; | ||
587 | } | ||
588 | |||
589 | if (!dec_en) | ||
590 | return 0; | ||
591 | |||
592 | if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) { | ||
593 | dev_err(dev, | ||
594 | "image width to be more than %d for decimation\n", | ||
595 | VPFE_IPIPE_MAX_INPUT_WIDTH); | ||
596 | return -EINVAL; | ||
597 | } | ||
598 | |||
599 | if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { | ||
600 | dev_err(dev, "rsz range is %d to %d\n", | ||
601 | IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | /* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame | ||
609 | * division parameters for resizer. | ||
610 | * in normal mode. | ||
611 | */ | ||
612 | static int | ||
613 | resizer_calculate_normal_f_div_param(struct device *dev, int input_width, | ||
614 | int output_width, struct resizer_scale_param *param) | ||
615 | { | ||
616 | /* rsz = R, input_width = H, output width = h in the equation */ | ||
617 | unsigned int val1; | ||
618 | unsigned int rsz; | ||
619 | unsigned int val; | ||
620 | unsigned int h1; | ||
621 | unsigned int h2; | ||
622 | unsigned int o; | ||
623 | |||
624 | if (output_width > input_width) { | ||
625 | dev_err(dev, "frame div mode is used for scale down only\n"); | ||
626 | return -EINVAL; | ||
627 | } | ||
628 | |||
629 | rsz = (input_width << 8) / output_width; | ||
630 | val = rsz << 1; | ||
631 | val = ((input_width << 8) / val) + 1; | ||
632 | o = 14; | ||
633 | if (!(val % 2)) { | ||
634 | h1 = val; | ||
635 | } else { | ||
636 | val = (input_width << 7); | ||
637 | val -= rsz >> 1; | ||
638 | val /= rsz << 1; | ||
639 | val <<= 1; | ||
640 | val += 2; | ||
641 | o += ((CEIL(rsz, 1024)) << 1); | ||
642 | h1 = val; | ||
643 | } | ||
644 | h2 = output_width - h1; | ||
645 | /* phi */ | ||
646 | val = (h1 * rsz) - (((input_width >> 1) - o) << 8); | ||
647 | /* skip */ | ||
648 | val1 = ((val - 1024) >> 9) << 1; | ||
649 | param->f_div.num_passes = MAX_PASSES; | ||
650 | param->f_div.pass[0].o_hsz = h1 - 1; | ||
651 | param->f_div.pass[0].i_hps = 0; | ||
652 | param->f_div.pass[0].h_phs = 0; | ||
653 | param->f_div.pass[0].src_hps = 0; | ||
654 | param->f_div.pass[0].src_hsz = (input_width >> 2) + o; | ||
655 | param->f_div.pass[1].o_hsz = h2 - 1; | ||
656 | param->f_div.pass[1].i_hps = val1; | ||
657 | param->f_div.pass[1].h_phs = (val - (val1 << 8)); | ||
658 | param->f_div.pass[1].src_hps = (input_width >> 2) - o; | ||
659 | param->f_div.pass[1].src_hsz = (input_width >> 2) + o; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static int | ||
665 | resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer) | ||
666 | { | ||
667 | struct vpfe_rsz_config_params *config = &resizer->config.user_config; | ||
668 | struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; | ||
669 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
670 | struct v4l2_mbus_framefmt *outformat1, *outformat2; | ||
671 | struct resizer_params *param = &resizer->config; | ||
672 | struct v4l2_mbus_framefmt *informat; | ||
673 | int decimation; | ||
674 | int line_len_c; | ||
675 | int line_len; | ||
676 | int rsz; | ||
677 | int ret; | ||
678 | |||
679 | informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; | ||
680 | outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; | ||
681 | outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; | ||
682 | |||
683 | decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev); | ||
684 | rsz = vpfe_ipipeif_get_rsz(vpfe_dev); | ||
685 | if (decimation && param->user_config.frame_div_mode_en) { | ||
686 | dev_err(dev, | ||
687 | "dec_en & frame_div_mode_en cannot enabled simultaneously\n"); | ||
688 | return -EINVAL; | ||
689 | } | ||
690 | |||
691 | ret = resizer_validate_decimation(dev, decimation, rsz, | ||
692 | param->user_config.frame_div_mode_en, informat->width); | ||
693 | if (ret) | ||
694 | return -EINVAL; | ||
695 | |||
696 | ret = resizer_validate_input_image_format(dev, informat->code, | ||
697 | informat->width, informat->height, &line_len); | ||
698 | if (ret) | ||
699 | return -EINVAL; | ||
700 | |||
701 | if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { | ||
702 | param->rsz_en[RSZ_A] = ENABLE; | ||
703 | ret = resizer_validate_output_image_format(dev, outformat1, | ||
704 | &line_len, &line_len_c); | ||
705 | if (ret) | ||
706 | return ret; | ||
707 | param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; | ||
708 | param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; | ||
709 | configure_resizer_out_params(resizer, RSZ_A, | ||
710 | ¶m->user_config.output1, 0, 1); | ||
711 | |||
712 | if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12) | ||
713 | param->rsz_common.raw_flip = 1; | ||
714 | else | ||
715 | param->rsz_common.raw_flip = 0; | ||
716 | |||
717 | if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) | ||
718 | resizer_enable_422_420_conversion(param, | ||
719 | RSZ_A, ENABLE); | ||
720 | else | ||
721 | resizer_enable_422_420_conversion(param, | ||
722 | RSZ_A, DISABLE); | ||
723 | } | ||
724 | |||
725 | if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { | ||
726 | param->rsz_en[RSZ_B] = ENABLE; | ||
727 | ret = resizer_validate_output_image_format(dev, outformat2, | ||
728 | &line_len, &line_len_c); | ||
729 | if (ret) | ||
730 | return ret; | ||
731 | param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; | ||
732 | param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; | ||
733 | configure_resizer_out_params(resizer, RSZ_B, | ||
734 | ¶m->user_config.output2, 0, 1); | ||
735 | if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) | ||
736 | resizer_enable_422_420_conversion(param, | ||
737 | RSZ_B, ENABLE); | ||
738 | else | ||
739 | resizer_enable_422_420_conversion(param, | ||
740 | RSZ_B, DISABLE); | ||
741 | } | ||
742 | |||
743 | resizer_configure_common_in_params(resizer); | ||
744 | if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { | ||
745 | resizer_calculate_resize_ratios(resizer, RSZ_A); | ||
746 | resizer_calculate_sdram_offsets(resizer, RSZ_A); | ||
747 | /* Overriding resize ratio calculation */ | ||
748 | if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { | ||
749 | param->rsz_rsc_param[RSZ_A].v_dif = | ||
750 | (((informat->height + 1) * 2) * 256) / | ||
751 | (param->rsz_rsc_param[RSZ_A].o_vsz + 1); | ||
752 | } | ||
753 | } | ||
754 | |||
755 | if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { | ||
756 | resizer_calculate_resize_ratios(resizer, RSZ_B); | ||
757 | resizer_calculate_sdram_offsets(resizer, RSZ_B); | ||
758 | /* Overriding resize ratio calculation */ | ||
759 | if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { | ||
760 | param->rsz_rsc_param[RSZ_B].v_dif = | ||
761 | (((informat->height + 1) * 2) * 256) / | ||
762 | (param->rsz_rsc_param[RSZ_B].o_vsz + 1); | ||
763 | } | ||
764 | } | ||
765 | if (param->user_config.frame_div_mode_en && | ||
766 | param->rsz_en[RSZ_A]) { | ||
767 | if (!param->rsz_rsc_param[RSZ_A].dscale_en) | ||
768 | ret = resizer_calculate_normal_f_div_param(dev, | ||
769 | informat->width, | ||
770 | param->rsz_rsc_param[RSZ_A].o_vsz + 1, | ||
771 | ¶m->rsz_rsc_param[RSZ_A]); | ||
772 | else | ||
773 | ret = resizer_calculate_down_scale_f_div_param(dev, | ||
774 | informat->width, | ||
775 | param->rsz_rsc_param[RSZ_A].o_vsz + 1, | ||
776 | ¶m->rsz_rsc_param[RSZ_A]); | ||
777 | if (ret) | ||
778 | return -EINVAL; | ||
779 | } | ||
780 | if (param->user_config.frame_div_mode_en && | ||
781 | param->rsz_en[RSZ_B]) { | ||
782 | if (!param->rsz_rsc_param[RSZ_B].dscale_en) | ||
783 | ret = resizer_calculate_normal_f_div_param(dev, | ||
784 | informat->width, | ||
785 | param->rsz_rsc_param[RSZ_B].o_vsz + 1, | ||
786 | ¶m->rsz_rsc_param[RSZ_B]); | ||
787 | else | ||
788 | ret = resizer_calculate_down_scale_f_div_param(dev, | ||
789 | informat->width, | ||
790 | param->rsz_rsc_param[RSZ_B].o_vsz + 1, | ||
791 | ¶m->rsz_rsc_param[RSZ_B]); | ||
792 | if (ret) | ||
793 | return -EINVAL; | ||
794 | } | ||
795 | param->rsz_common.passthrough = config->bypass; | ||
796 | if (config->bypass) | ||
797 | resizer_configure_passthru(resizer, 1); | ||
798 | return 0; | ||
799 | } | ||
800 | |||
801 | static void | ||
802 | resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer) | ||
803 | { | ||
804 | #define WIDTH_I 640 | ||
805 | #define HEIGHT_I 480 | ||
806 | #define WIDTH_O 640 | ||
807 | #define HEIGHT_O 480 | ||
808 | const struct resizer_params rsz_default_config = { | ||
809 | .oper_mode = RESIZER_MODE_ONE_SHOT, | ||
810 | .rsz_common = { | ||
811 | .vsz = HEIGHT_I - 1, | ||
812 | .hsz = WIDTH_I - 1, | ||
813 | .src_img_fmt = RSZ_IMG_422, | ||
814 | .raw_flip = 1, /* flip preserve Raw format */ | ||
815 | .source = IPIPE_DATA, | ||
816 | .passthrough = BYPASS_OFF, | ||
817 | .yuv_y_max = 255, | ||
818 | .yuv_c_max = 255, | ||
819 | .rsz_seq_crv = DISABLE, | ||
820 | .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, | ||
821 | }, | ||
822 | .rsz_rsc_param = { | ||
823 | { | ||
824 | .h_flip = DISABLE, | ||
825 | .v_flip = DISABLE, | ||
826 | .cen = DISABLE, | ||
827 | .yen = DISABLE, | ||
828 | .o_vsz = HEIGHT_O - 1, | ||
829 | .o_hsz = WIDTH_O - 1, | ||
830 | .v_dif = 256, | ||
831 | .v_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
832 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
833 | .h_dif = 256, | ||
834 | .h_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
835 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
836 | .h_dscale_ave_sz = | ||
837 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
838 | .v_dscale_ave_sz = | ||
839 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
840 | }, | ||
841 | { | ||
842 | .h_flip = DISABLE, | ||
843 | .v_flip = DISABLE, | ||
844 | .cen = DISABLE, | ||
845 | .yen = DISABLE, | ||
846 | .o_vsz = HEIGHT_O - 1, | ||
847 | .o_hsz = WIDTH_O - 1, | ||
848 | .v_dif = 256, | ||
849 | .v_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
850 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
851 | .h_dif = 256, | ||
852 | .h_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
853 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
854 | .h_dscale_ave_sz = | ||
855 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
856 | .v_dscale_ave_sz = | ||
857 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
858 | }, | ||
859 | }, | ||
860 | .rsz2rgb = { | ||
861 | { | ||
862 | .rgb_en = DISABLE | ||
863 | }, | ||
864 | { | ||
865 | .rgb_en = DISABLE | ||
866 | } | ||
867 | }, | ||
868 | .ext_mem_param = { | ||
869 | { | ||
870 | .rsz_sdr_oft_y = WIDTH_O << 1, | ||
871 | .rsz_sdr_ptr_e_y = HEIGHT_O, | ||
872 | .rsz_sdr_oft_c = WIDTH_O, | ||
873 | .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, | ||
874 | }, | ||
875 | { | ||
876 | .rsz_sdr_oft_y = WIDTH_O << 1, | ||
877 | .rsz_sdr_ptr_e_y = HEIGHT_O, | ||
878 | .rsz_sdr_oft_c = WIDTH_O, | ||
879 | .rsz_sdr_ptr_e_c = HEIGHT_O, | ||
880 | }, | ||
881 | }, | ||
882 | .rsz_en[0] = ENABLE, | ||
883 | .rsz_en[1] = DISABLE, | ||
884 | .user_config = { | ||
885 | .output1 = { | ||
886 | .v_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
887 | .v_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
888 | .h_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
889 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
890 | .h_dscale_ave_sz = | ||
891 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
892 | .v_dscale_ave_sz = | ||
893 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
894 | }, | ||
895 | .output2 = { | ||
896 | .v_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
897 | .v_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
898 | .h_typ_y = VPFE_RSZ_INTP_CUBIC, | ||
899 | .h_typ_c = VPFE_RSZ_INTP_CUBIC, | ||
900 | .h_dscale_ave_sz = | ||
901 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
902 | .v_dscale_ave_sz = | ||
903 | VPFE_IPIPE_DWN_SCALE_1_OVER_2, | ||
904 | }, | ||
905 | .yuv_y_max = 255, | ||
906 | .yuv_c_max = 255, | ||
907 | .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, | ||
908 | }, | ||
909 | }; | ||
910 | memset(&resizer->config, 0, sizeof(struct resizer_params)); | ||
911 | memcpy(&resizer->config, &rsz_default_config, | ||
912 | sizeof(struct resizer_params)); | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * resizer_set_configuration() - set resizer config | ||
917 | * @resizer: vpfe resizer device pointer. | ||
918 | * @chan_config: resizer channel configuration. | ||
919 | */ | ||
920 | static int | ||
921 | resizer_set_configuration(struct vpfe_resizer_device *resizer, | ||
922 | struct vpfe_rsz_config *chan_config) | ||
923 | { | ||
924 | if (!chan_config->config) | ||
925 | resizer_set_defualt_configuration(resizer); | ||
926 | else | ||
927 | if (copy_from_user(&resizer->config.user_config, | ||
928 | chan_config->config, sizeof(struct vpfe_rsz_config_params))) | ||
929 | return -EFAULT; | ||
930 | |||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | /* | ||
935 | * resizer_get_configuration() - get resizer config | ||
936 | * @resizer: vpfe resizer device pointer. | ||
937 | * @channel: image processor logical channel. | ||
938 | * @chan_config: resizer channel configuration. | ||
939 | */ | ||
940 | static int | ||
941 | resizer_get_configuration(struct vpfe_resizer_device *resizer, | ||
942 | struct vpfe_rsz_config *chan_config) | ||
943 | { | ||
944 | struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; | ||
945 | |||
946 | if (!chan_config->config) { | ||
947 | dev_err(dev, "Resizer channel invalid pointer\n"); | ||
948 | return -EINVAL; | ||
949 | } | ||
950 | |||
951 | if (copy_to_user((void *)chan_config->config, | ||
952 | (void *)&resizer->config.user_config, | ||
953 | sizeof(struct vpfe_rsz_config_params))) { | ||
954 | dev_err(dev, "resizer_get_configuration: Error in copy to user\n"); | ||
955 | return -EFAULT; | ||
956 | } | ||
957 | |||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * VPFE video operations | ||
963 | */ | ||
964 | |||
965 | /* | ||
966 | * resizer_a_video_out_queue() - RESIZER-A video out queue | ||
967 | * @vpfe_dev: vpfe device pointer. | ||
968 | * @addr: buffer address. | ||
969 | */ | ||
970 | static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev, | ||
971 | unsigned long addr) | ||
972 | { | ||
973 | struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; | ||
974 | |||
975 | return resizer_set_outaddr(resizer->base_addr, | ||
976 | &resizer->config, RSZ_A, addr); | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * resizer_b_video_out_queue() - RESIZER-B video out queue | ||
981 | * @vpfe_dev: vpfe device pointer. | ||
982 | * @addr: buffer address. | ||
983 | */ | ||
984 | static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev, | ||
985 | unsigned long addr) | ||
986 | { | ||
987 | struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; | ||
988 | |||
989 | return resizer_set_outaddr(resizer->base_addr, | ||
990 | &resizer->config, RSZ_B, addr); | ||
991 | } | ||
992 | |||
993 | static const struct vpfe_video_operations resizer_a_video_ops = { | ||
994 | .queue = resizer_a_video_out_queue, | ||
995 | }; | ||
996 | |||
997 | static const struct vpfe_video_operations resizer_b_video_ops = { | ||
998 | .queue = resizer_b_video_out_queue, | ||
999 | }; | ||
1000 | |||
1001 | static void resizer_enable(struct vpfe_resizer_device *resizer, int en) | ||
1002 | { | ||
1003 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1004 | u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; | ||
1005 | unsigned char val; | ||
1006 | |||
1007 | if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) | ||
1008 | return; | ||
1009 | |||
1010 | if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF && | ||
1011 | ipipeif_sink == IPIPEIF_INPUT_MEMORY) { | ||
1012 | do { | ||
1013 | val = regr_rsz(resizer->base_addr, RSZ_SRC_EN); | ||
1014 | } while (val); | ||
1015 | |||
1016 | if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { | ||
1017 | do { | ||
1018 | val = regr_rsz(resizer->base_addr, RSZ_A); | ||
1019 | } while (val); | ||
1020 | } | ||
1021 | if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { | ||
1022 | do { | ||
1023 | val = regr_rsz(resizer->base_addr, RSZ_B); | ||
1024 | } while (val); | ||
1025 | } | ||
1026 | } | ||
1027 | if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) | ||
1028 | rsz_enable(resizer->base_addr, RSZ_A, en); | ||
1029 | |||
1030 | if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) | ||
1031 | rsz_enable(resizer->base_addr, RSZ_B, en); | ||
1032 | } | ||
1033 | |||
1034 | |||
1035 | /* | ||
1036 | * resizer_ss_isr() - resizer module single-shot buffer scheduling isr | ||
1037 | * @resizer: vpfe resizer device pointer. | ||
1038 | */ | ||
1039 | static void resizer_ss_isr(struct vpfe_resizer_device *resizer) | ||
1040 | { | ||
1041 | struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; | ||
1042 | struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; | ||
1043 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1044 | struct vpfe_pipeline *pipe = &video_out->pipe; | ||
1045 | u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; | ||
1046 | u32 val; | ||
1047 | |||
1048 | if (ipipeif_sink != IPIPEIF_INPUT_MEMORY) | ||
1049 | return; | ||
1050 | |||
1051 | if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { | ||
1052 | val = vpss_dma_complete_interrupt(); | ||
1053 | if (val != 0 && val != 2) | ||
1054 | return; | ||
1055 | } | ||
1056 | |||
1057 | if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { | ||
1058 | spin_lock(&video_out->dma_queue_lock); | ||
1059 | vpfe_video_process_buffer_complete(video_out); | ||
1060 | video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; | ||
1061 | vpfe_video_schedule_next_buffer(video_out); | ||
1062 | spin_unlock(&video_out->dma_queue_lock); | ||
1063 | } | ||
1064 | |||
1065 | /* If resizer B is enabled */ | ||
1066 | if (pipe->output_num > 1 && resizer->resizer_b.output == | ||
1067 | RESIZER_OUPUT_MEMORY) { | ||
1068 | spin_lock(&video_out->dma_queue_lock); | ||
1069 | vpfe_video_process_buffer_complete(video_out2); | ||
1070 | video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; | ||
1071 | vpfe_video_schedule_next_buffer(video_out2); | ||
1072 | spin_unlock(&video_out2->dma_queue_lock); | ||
1073 | } | ||
1074 | |||
1075 | /* start HW if buffers are queued */ | ||
1076 | if (vpfe_video_is_pipe_ready(pipe) && | ||
1077 | resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { | ||
1078 | resizer_enable(resizer, 1); | ||
1079 | vpfe_ipipe_enable(vpfe_dev, 1); | ||
1080 | vpfe_ipipeif_enable(vpfe_dev); | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | /* | ||
1085 | * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr | ||
1086 | * @resizer: vpfe resizer device pointer. | ||
1087 | */ | ||
1088 | void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer) | ||
1089 | { | ||
1090 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1091 | struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; | ||
1092 | struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; | ||
1093 | struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe; | ||
1094 | enum v4l2_field field; | ||
1095 | int fid; | ||
1096 | |||
1097 | if (!video_out->started) | ||
1098 | return; | ||
1099 | |||
1100 | if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) | ||
1101 | return; | ||
1102 | |||
1103 | field = video_out->fmt.fmt.pix.field; | ||
1104 | if (field == V4L2_FIELD_NONE) { | ||
1105 | /* handle progressive frame capture */ | ||
1106 | if (video_out->cur_frm != video_out->next_frm) { | ||
1107 | vpfe_video_process_buffer_complete(video_out); | ||
1108 | if (pipe->output_num > 1) | ||
1109 | vpfe_video_process_buffer_complete(video_out2); | ||
1110 | } | ||
1111 | |||
1112 | video_out->skip_frame_count--; | ||
1113 | if (!video_out->skip_frame_count) { | ||
1114 | video_out->skip_frame_count = | ||
1115 | video_out->skip_frame_count_init; | ||
1116 | rsz_src_enable(resizer->base_addr, 1); | ||
1117 | } else { | ||
1118 | rsz_src_enable(resizer->base_addr, 0); | ||
1119 | } | ||
1120 | return; | ||
1121 | } | ||
1122 | |||
1123 | /* handle interlaced frame capture */ | ||
1124 | fid = vpfe_isif_get_fid(vpfe_dev); | ||
1125 | |||
1126 | /* switch the software maintained field id */ | ||
1127 | video_out->field_id ^= 1; | ||
1128 | if (fid == video_out->field_id) { | ||
1129 | /* | ||
1130 | * we are in-sync here,continue. | ||
1131 | * One frame is just being captured. If the | ||
1132 | * next frame is available, release the current | ||
1133 | * frame and move on | ||
1134 | */ | ||
1135 | if (fid == 0 && video_out->cur_frm != video_out->next_frm) { | ||
1136 | vpfe_video_process_buffer_complete(video_out); | ||
1137 | if (pipe->output_num > 1) | ||
1138 | vpfe_video_process_buffer_complete(video_out2); | ||
1139 | } | ||
1140 | } else if (fid == 0) { | ||
1141 | /* | ||
1142 | * out of sync. Recover from any hardware out-of-sync. | ||
1143 | * May loose one frame | ||
1144 | */ | ||
1145 | video_out->field_id = fid; | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | /* | ||
1150 | * vpfe_resizer_dma_isr() - resizer module dma isr | ||
1151 | * @resizer: vpfe resizer device pointer. | ||
1152 | */ | ||
1153 | void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer) | ||
1154 | { | ||
1155 | struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; | ||
1156 | struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; | ||
1157 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1158 | struct vpfe_pipeline *pipe = &video_out->pipe; | ||
1159 | int schedule_capture = 0; | ||
1160 | enum v4l2_field field; | ||
1161 | int fid; | ||
1162 | |||
1163 | if (!video_out->started) | ||
1164 | return; | ||
1165 | |||
1166 | if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { | ||
1167 | resizer_ss_isr(resizer); | ||
1168 | return; | ||
1169 | } | ||
1170 | |||
1171 | field = video_out->fmt.fmt.pix.field; | ||
1172 | if (field == V4L2_FIELD_NONE) { | ||
1173 | if (!list_empty(&video_out->dma_queue) && | ||
1174 | video_out->cur_frm == video_out->next_frm) | ||
1175 | schedule_capture = 1; | ||
1176 | } else { | ||
1177 | fid = vpfe_isif_get_fid(vpfe_dev); | ||
1178 | if (fid == video_out->field_id) { | ||
1179 | /* we are in-sync here,continue */ | ||
1180 | if (fid == 1 && !list_empty(&video_out->dma_queue) && | ||
1181 | video_out->cur_frm == video_out->next_frm) | ||
1182 | schedule_capture = 1; | ||
1183 | } | ||
1184 | } | ||
1185 | |||
1186 | if (!schedule_capture) | ||
1187 | return; | ||
1188 | |||
1189 | spin_lock(&video_out->dma_queue_lock); | ||
1190 | vpfe_video_schedule_next_buffer(video_out); | ||
1191 | spin_unlock(&video_out->dma_queue_lock); | ||
1192 | if (pipe->output_num > 1) { | ||
1193 | spin_lock(&video_out2->dma_queue_lock); | ||
1194 | vpfe_video_schedule_next_buffer(video_out2); | ||
1195 | spin_unlock(&video_out2->dma_queue_lock); | ||
1196 | } | ||
1197 | } | ||
1198 | |||
1199 | /* | ||
1200 | * V4L2 subdev operations | ||
1201 | */ | ||
1202 | |||
1203 | /* | ||
1204 | * resizer_ioctl() - Handle resizer module private ioctl's | ||
1205 | * @sd: pointer to v4l2 subdev structure | ||
1206 | * @cmd: configuration command | ||
1207 | * @arg: configuration argument | ||
1208 | */ | ||
1209 | static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) | ||
1210 | { | ||
1211 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1212 | struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; | ||
1213 | struct vpfe_rsz_config *user_config; | ||
1214 | int ret = -ENOIOCTLCMD; | ||
1215 | |||
1216 | if (&resizer->crop_resizer.subdev != sd) | ||
1217 | return ret; | ||
1218 | |||
1219 | switch (cmd) { | ||
1220 | case VIDIOC_VPFE_RSZ_S_CONFIG: | ||
1221 | user_config = (struct vpfe_rsz_config *)arg; | ||
1222 | ret = resizer_set_configuration(resizer, user_config); | ||
1223 | break; | ||
1224 | |||
1225 | case VIDIOC_VPFE_RSZ_G_CONFIG: | ||
1226 | user_config = (struct vpfe_rsz_config *)arg; | ||
1227 | if (!user_config->config) { | ||
1228 | dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); | ||
1229 | return -EINVAL; | ||
1230 | } | ||
1231 | ret = resizer_get_configuration(resizer, user_config); | ||
1232 | break; | ||
1233 | } | ||
1234 | return ret; | ||
1235 | } | ||
1236 | |||
1237 | static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer) | ||
1238 | { | ||
1239 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1240 | u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; | ||
1241 | u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; | ||
1242 | struct resizer_params *param = &resizer->config; | ||
1243 | int ret = 0; | ||
1244 | |||
1245 | if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY || | ||
1246 | resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { | ||
1247 | if (ipipeif_sink == IPIPEIF_INPUT_MEMORY && | ||
1248 | ipipeif_source == IPIPEIF_OUTPUT_RESIZER) | ||
1249 | ret = resizer_configure_in_single_shot_mode(resizer); | ||
1250 | else | ||
1251 | ret = resizer_configure_in_continious_mode(resizer); | ||
1252 | if (ret) | ||
1253 | return ret; | ||
1254 | ret = config_rsz_hw(resizer, param); | ||
1255 | } | ||
1256 | return ret; | ||
1257 | } | ||
1258 | |||
1259 | /* | ||
1260 | * resizer_set_stream() - Enable/Disable streaming on resizer subdev | ||
1261 | * @sd: pointer to v4l2 subdev structure | ||
1262 | * @enable: 1 == Enable, 0 == Disable | ||
1263 | */ | ||
1264 | static int resizer_set_stream(struct v4l2_subdev *sd, int enable) | ||
1265 | { | ||
1266 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1267 | |||
1268 | if (&resizer->crop_resizer.subdev != sd) | ||
1269 | return 0; | ||
1270 | |||
1271 | if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) | ||
1272 | return 0; | ||
1273 | |||
1274 | switch (enable) { | ||
1275 | case 1: | ||
1276 | if (resizer_do_hw_setup(resizer) < 0) | ||
1277 | return -EINVAL; | ||
1278 | resizer_enable(resizer, enable); | ||
1279 | break; | ||
1280 | |||
1281 | case 0: | ||
1282 | resizer_enable(resizer, enable); | ||
1283 | break; | ||
1284 | } | ||
1285 | |||
1286 | return 0; | ||
1287 | } | ||
1288 | |||
1289 | /* | ||
1290 | * __resizer_get_format() - helper function for getting resizer format | ||
1291 | * @sd: pointer to subdev. | ||
1292 | * @fh: V4L2 subdev file handle. | ||
1293 | * @pad: pad number. | ||
1294 | * @which: wanted subdev format. | ||
1295 | * Retun wanted mbus frame format. | ||
1296 | */ | ||
1297 | static struct v4l2_mbus_framefmt * | ||
1298 | __resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1299 | unsigned int pad, enum v4l2_subdev_format_whence which) | ||
1300 | { | ||
1301 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1302 | |||
1303 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
1304 | return v4l2_subdev_get_try_format(fh, pad); | ||
1305 | if (&resizer->crop_resizer.subdev == sd) | ||
1306 | return &resizer->crop_resizer.formats[pad]; | ||
1307 | if (&resizer->resizer_a.subdev == sd) | ||
1308 | return &resizer->resizer_a.formats[pad]; | ||
1309 | if (&resizer->resizer_b.subdev == sd) | ||
1310 | return &resizer->resizer_b.formats[pad]; | ||
1311 | return NULL; | ||
1312 | } | ||
1313 | |||
1314 | /* | ||
1315 | * resizer_try_format() - Handle try format by pad subdev method | ||
1316 | * @sd: pointer to subdev. | ||
1317 | * @fh: V4L2 subdev file handle. | ||
1318 | * @pad: pad num. | ||
1319 | * @fmt: pointer to v4l2 format structure. | ||
1320 | * @which: wanted subdev format. | ||
1321 | */ | ||
1322 | static void | ||
1323 | resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1324 | unsigned int pad, struct v4l2_mbus_framefmt *fmt, | ||
1325 | enum v4l2_subdev_format_whence which) | ||
1326 | { | ||
1327 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1328 | unsigned int max_out_height; | ||
1329 | unsigned int max_out_width; | ||
1330 | unsigned int i; | ||
1331 | |||
1332 | if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) || | ||
1333 | (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) || | ||
1334 | (&resizer->crop_resizer.subdev == sd && | ||
1335 | (pad == RESIZER_CROP_PAD_SOURCE || | ||
1336 | pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) { | ||
1337 | for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) { | ||
1338 | if (fmt->code == resizer_input_formats[i]) | ||
1339 | break; | ||
1340 | } | ||
1341 | /* If not found, use UYVY as default */ | ||
1342 | if (i >= ARRAY_SIZE(resizer_input_formats)) | ||
1343 | fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1344 | |||
1345 | fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, | ||
1346 | MAX_IN_WIDTH); | ||
1347 | fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, | ||
1348 | MAX_IN_HEIGHT); | ||
1349 | } else if (&resizer->resizer_a.subdev == sd && | ||
1350 | pad == RESIZER_PAD_SOURCE) { | ||
1351 | max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; | ||
1352 | max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; | ||
1353 | |||
1354 | for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { | ||
1355 | if (fmt->code == resizer_output_formats[i]) | ||
1356 | break; | ||
1357 | } | ||
1358 | /* If not found, use UYVY as default */ | ||
1359 | if (i >= ARRAY_SIZE(resizer_output_formats)) | ||
1360 | fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1361 | |||
1362 | fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, | ||
1363 | max_out_width); | ||
1364 | fmt->width &= ~15; | ||
1365 | fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, | ||
1366 | max_out_height); | ||
1367 | } else if (&resizer->resizer_b.subdev == sd && | ||
1368 | pad == RESIZER_PAD_SOURCE) { | ||
1369 | max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B; | ||
1370 | max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B; | ||
1371 | |||
1372 | for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { | ||
1373 | if (fmt->code == resizer_output_formats[i]) | ||
1374 | break; | ||
1375 | } | ||
1376 | /* If not found, use UYVY as default */ | ||
1377 | if (i >= ARRAY_SIZE(resizer_output_formats)) | ||
1378 | fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1379 | |||
1380 | fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, | ||
1381 | max_out_width); | ||
1382 | fmt->width &= ~15; | ||
1383 | fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, | ||
1384 | max_out_height); | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | /* | ||
1389 | * resizer_set_format() - Handle set format by pads subdev method | ||
1390 | * @sd: pointer to v4l2 subdev structure | ||
1391 | * @fh: V4L2 subdev file handle | ||
1392 | * @fmt: pointer to v4l2 subdev format structure | ||
1393 | * return -EINVAL or zero on success | ||
1394 | */ | ||
1395 | static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1396 | struct v4l2_subdev_format *fmt) | ||
1397 | { | ||
1398 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1399 | struct v4l2_mbus_framefmt *format; | ||
1400 | |||
1401 | format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); | ||
1402 | if (format == NULL) | ||
1403 | return -EINVAL; | ||
1404 | |||
1405 | resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); | ||
1406 | *format = fmt->format; | ||
1407 | |||
1408 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) | ||
1409 | return 0; | ||
1410 | |||
1411 | if (&resizer->crop_resizer.subdev == sd) { | ||
1412 | if (fmt->pad == RESIZER_CROP_PAD_SINK) { | ||
1413 | resizer->crop_resizer.formats[fmt->pad] = fmt->format; | ||
1414 | } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE && | ||
1415 | resizer->crop_resizer.output == RESIZER_A) { | ||
1416 | resizer->crop_resizer.formats[fmt->pad] = fmt->format; | ||
1417 | resizer->crop_resizer. | ||
1418 | formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format; | ||
1419 | } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 && | ||
1420 | resizer->crop_resizer.output2 == RESIZER_B) { | ||
1421 | resizer->crop_resizer.formats[fmt->pad] = fmt->format; | ||
1422 | resizer->crop_resizer. | ||
1423 | formats[RESIZER_CROP_PAD_SOURCE] = fmt->format; | ||
1424 | } else { | ||
1425 | return -EINVAL; | ||
1426 | } | ||
1427 | } else if (&resizer->resizer_a.subdev == sd) { | ||
1428 | if (fmt->pad == RESIZER_PAD_SINK) | ||
1429 | resizer->resizer_a.formats[fmt->pad] = fmt->format; | ||
1430 | else if (fmt->pad == RESIZER_PAD_SOURCE) | ||
1431 | resizer->resizer_a.formats[fmt->pad] = fmt->format; | ||
1432 | else | ||
1433 | return -EINVAL; | ||
1434 | } else if (&resizer->resizer_b.subdev == sd) { | ||
1435 | if (fmt->pad == RESIZER_PAD_SINK) | ||
1436 | resizer->resizer_b.formats[fmt->pad] = fmt->format; | ||
1437 | else if (fmt->pad == RESIZER_PAD_SOURCE) | ||
1438 | resizer->resizer_b.formats[fmt->pad] = fmt->format; | ||
1439 | else | ||
1440 | return -EINVAL; | ||
1441 | } else { | ||
1442 | return -EINVAL; | ||
1443 | } | ||
1444 | |||
1445 | return 0; | ||
1446 | } | ||
1447 | |||
1448 | /* | ||
1449 | * resizer_get_format() - Retrieve the video format on a pad | ||
1450 | * @sd: pointer to v4l2 subdev structure. | ||
1451 | * @fh: V4L2 subdev file handle. | ||
1452 | * @fmt: pointer to v4l2 subdev format structure | ||
1453 | * return -EINVAL or zero on success | ||
1454 | */ | ||
1455 | static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | ||
1456 | struct v4l2_subdev_format *fmt) | ||
1457 | { | ||
1458 | struct v4l2_mbus_framefmt *format; | ||
1459 | |||
1460 | format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); | ||
1461 | if (format == NULL) | ||
1462 | return -EINVAL; | ||
1463 | |||
1464 | fmt->format = *format; | ||
1465 | |||
1466 | return 0; | ||
1467 | } | ||
1468 | |||
1469 | /* | ||
1470 | * resizer_enum_frame_size() - enum frame sizes on pads | ||
1471 | * @sd: Pointer to subdevice. | ||
1472 | * @fh: V4L2 subdev file handle. | ||
1473 | * @code: pointer to v4l2_subdev_frame_size_enum structure. | ||
1474 | */ | ||
1475 | static int resizer_enum_frame_size(struct v4l2_subdev *sd, | ||
1476 | struct v4l2_subdev_fh *fh, | ||
1477 | struct v4l2_subdev_frame_size_enum *fse) | ||
1478 | { | ||
1479 | struct v4l2_mbus_framefmt format; | ||
1480 | |||
1481 | if (fse->index != 0) | ||
1482 | return -EINVAL; | ||
1483 | |||
1484 | format.code = fse->code; | ||
1485 | format.width = 1; | ||
1486 | format.height = 1; | ||
1487 | resizer_try_format(sd, fh, fse->pad, &format, | ||
1488 | V4L2_SUBDEV_FORMAT_TRY); | ||
1489 | fse->min_width = format.width; | ||
1490 | fse->min_height = format.height; | ||
1491 | |||
1492 | if (format.code != fse->code) | ||
1493 | return -EINVAL; | ||
1494 | |||
1495 | format.code = fse->code; | ||
1496 | format.width = -1; | ||
1497 | format.height = -1; | ||
1498 | resizer_try_format(sd, fh, fse->pad, &format, | ||
1499 | V4L2_SUBDEV_FORMAT_TRY); | ||
1500 | fse->max_width = format.width; | ||
1501 | fse->max_height = format.height; | ||
1502 | |||
1503 | return 0; | ||
1504 | } | ||
1505 | |||
1506 | /* | ||
1507 | * resizer_enum_mbus_code() - enum mbus codes for pads | ||
1508 | * @sd: Pointer to subdevice. | ||
1509 | * @fh: V4L2 subdev file handle | ||
1510 | * @code: pointer to v4l2_subdev_mbus_code_enum structure | ||
1511 | */ | ||
1512 | static int resizer_enum_mbus_code(struct v4l2_subdev *sd, | ||
1513 | struct v4l2_subdev_fh *fh, | ||
1514 | struct v4l2_subdev_mbus_code_enum *code) | ||
1515 | { | ||
1516 | if (code->pad == RESIZER_PAD_SINK) { | ||
1517 | if (code->index >= ARRAY_SIZE(resizer_input_formats)) | ||
1518 | return -EINVAL; | ||
1519 | |||
1520 | code->code = resizer_input_formats[code->index]; | ||
1521 | } else if (code->pad == RESIZER_PAD_SOURCE) { | ||
1522 | if (code->index >= ARRAY_SIZE(resizer_output_formats)) | ||
1523 | return -EINVAL; | ||
1524 | |||
1525 | code->code = resizer_output_formats[code->index]; | ||
1526 | } | ||
1527 | |||
1528 | return 0; | ||
1529 | } | ||
1530 | |||
1531 | /* | ||
1532 | * resizer_init_formats() - Initialize formats on all pads | ||
1533 | * @sd: Pointer to subdevice. | ||
1534 | * @fh: V4L2 subdev file handle. | ||
1535 | * | ||
1536 | * Initialize all pad formats with default values. If fh is not NULL, try | ||
1537 | * formats are initialized on the file handle. Otherwise active formats are | ||
1538 | * initialized on the device. | ||
1539 | */ | ||
1540 | static int resizer_init_formats(struct v4l2_subdev *sd, | ||
1541 | struct v4l2_subdev_fh *fh) | ||
1542 | { | ||
1543 | __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; | ||
1544 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1545 | struct v4l2_subdev_format format; | ||
1546 | |||
1547 | if (&resizer->crop_resizer.subdev == sd) { | ||
1548 | memset(&format, 0, sizeof(format)); | ||
1549 | format.pad = RESIZER_CROP_PAD_SINK; | ||
1550 | format.which = which; | ||
1551 | format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; | ||
1552 | format.format.width = MAX_IN_WIDTH; | ||
1553 | format.format.height = MAX_IN_HEIGHT; | ||
1554 | resizer_set_format(sd, fh, &format); | ||
1555 | |||
1556 | memset(&format, 0, sizeof(format)); | ||
1557 | format.pad = RESIZER_CROP_PAD_SOURCE; | ||
1558 | format.which = which; | ||
1559 | format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1560 | format.format.width = MAX_IN_WIDTH; | ||
1561 | format.format.height = MAX_IN_WIDTH; | ||
1562 | resizer_set_format(sd, fh, &format); | ||
1563 | |||
1564 | memset(&format, 0, sizeof(format)); | ||
1565 | format.pad = RESIZER_CROP_PAD_SOURCE2; | ||
1566 | format.which = which; | ||
1567 | format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1568 | format.format.width = MAX_IN_WIDTH; | ||
1569 | format.format.height = MAX_IN_WIDTH; | ||
1570 | resizer_set_format(sd, fh, &format); | ||
1571 | } else if (&resizer->resizer_a.subdev == sd) { | ||
1572 | memset(&format, 0, sizeof(format)); | ||
1573 | format.pad = RESIZER_PAD_SINK; | ||
1574 | format.which = which; | ||
1575 | format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; | ||
1576 | format.format.width = MAX_IN_WIDTH; | ||
1577 | format.format.height = MAX_IN_HEIGHT; | ||
1578 | resizer_set_format(sd, fh, &format); | ||
1579 | |||
1580 | memset(&format, 0, sizeof(format)); | ||
1581 | format.pad = RESIZER_PAD_SOURCE; | ||
1582 | format.which = which; | ||
1583 | format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1584 | format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; | ||
1585 | format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; | ||
1586 | resizer_set_format(sd, fh, &format); | ||
1587 | } else if (&resizer->resizer_b.subdev == sd) { | ||
1588 | memset(&format, 0, sizeof(format)); | ||
1589 | format.pad = RESIZER_PAD_SINK; | ||
1590 | format.which = which; | ||
1591 | format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; | ||
1592 | format.format.width = MAX_IN_WIDTH; | ||
1593 | format.format.height = MAX_IN_HEIGHT; | ||
1594 | resizer_set_format(sd, fh, &format); | ||
1595 | |||
1596 | memset(&format, 0, sizeof(format)); | ||
1597 | format.pad = RESIZER_PAD_SOURCE; | ||
1598 | format.which = which; | ||
1599 | format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; | ||
1600 | format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; | ||
1601 | format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; | ||
1602 | resizer_set_format(sd, fh, &format); | ||
1603 | } | ||
1604 | |||
1605 | return 0; | ||
1606 | } | ||
1607 | |||
1608 | /* subdev core operations */ | ||
1609 | static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { | ||
1610 | .ioctl = resizer_ioctl, | ||
1611 | }; | ||
1612 | |||
1613 | /* subdev internal operations */ | ||
1614 | static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { | ||
1615 | .open = resizer_init_formats, | ||
1616 | }; | ||
1617 | |||
1618 | /* subdev video operations */ | ||
1619 | static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { | ||
1620 | .s_stream = resizer_set_stream, | ||
1621 | }; | ||
1622 | |||
1623 | /* subdev pad operations */ | ||
1624 | static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { | ||
1625 | .enum_mbus_code = resizer_enum_mbus_code, | ||
1626 | .enum_frame_size = resizer_enum_frame_size, | ||
1627 | .get_fmt = resizer_get_format, | ||
1628 | .set_fmt = resizer_set_format, | ||
1629 | }; | ||
1630 | |||
1631 | /* subdev operations */ | ||
1632 | static const struct v4l2_subdev_ops resizer_v4l2_ops = { | ||
1633 | .core = &resizer_v4l2_core_ops, | ||
1634 | .video = &resizer_v4l2_video_ops, | ||
1635 | .pad = &resizer_v4l2_pad_ops, | ||
1636 | }; | ||
1637 | |||
1638 | /* | ||
1639 | * Media entity operations | ||
1640 | */ | ||
1641 | |||
1642 | /* | ||
1643 | * resizer_link_setup() - Setup resizer connections | ||
1644 | * @entity: Pointer to media entity structure | ||
1645 | * @local: Pointer to local pad array | ||
1646 | * @remote: Pointer to remote pad array | ||
1647 | * @flags: Link flags | ||
1648 | * return -EINVAL or zero on success | ||
1649 | */ | ||
1650 | static int resizer_link_setup(struct media_entity *entity, | ||
1651 | const struct media_pad *local, | ||
1652 | const struct media_pad *remote, u32 flags) | ||
1653 | { | ||
1654 | struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); | ||
1655 | struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); | ||
1656 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1657 | u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; | ||
1658 | u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; | ||
1659 | |||
1660 | if (&resizer->crop_resizer.subdev == sd) { | ||
1661 | switch (local->index | media_entity_type(remote->entity)) { | ||
1662 | case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1663 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1664 | resizer->crop_resizer.input = | ||
1665 | RESIZER_CROP_INPUT_NONE; | ||
1666 | break; | ||
1667 | } | ||
1668 | |||
1669 | if (resizer->crop_resizer.input != | ||
1670 | RESIZER_CROP_INPUT_NONE) | ||
1671 | return -EBUSY; | ||
1672 | if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER) | ||
1673 | resizer->crop_resizer.input = | ||
1674 | RESIZER_CROP_INPUT_IPIPEIF; | ||
1675 | else if (ipipe_source == IPIPE_OUTPUT_RESIZER) | ||
1676 | resizer->crop_resizer.input = | ||
1677 | RESIZER_CROP_INPUT_IPIPE; | ||
1678 | else | ||
1679 | return -EINVAL; | ||
1680 | break; | ||
1681 | |||
1682 | case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1683 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1684 | resizer->crop_resizer.output = | ||
1685 | RESIZER_CROP_OUTPUT_NONE; | ||
1686 | break; | ||
1687 | } | ||
1688 | if (resizer->crop_resizer.output != | ||
1689 | RESIZER_CROP_OUTPUT_NONE) | ||
1690 | return -EBUSY; | ||
1691 | resizer->crop_resizer.output = RESIZER_A; | ||
1692 | break; | ||
1693 | |||
1694 | case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1695 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1696 | resizer->crop_resizer.output2 = | ||
1697 | RESIZER_CROP_OUTPUT_NONE; | ||
1698 | break; | ||
1699 | } | ||
1700 | if (resizer->crop_resizer.output2 != | ||
1701 | RESIZER_CROP_OUTPUT_NONE) | ||
1702 | return -EBUSY; | ||
1703 | resizer->crop_resizer.output2 = RESIZER_B; | ||
1704 | break; | ||
1705 | |||
1706 | default: | ||
1707 | return -EINVAL; | ||
1708 | } | ||
1709 | } else if (&resizer->resizer_a.subdev == sd) { | ||
1710 | switch (local->index | media_entity_type(remote->entity)) { | ||
1711 | case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1712 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1713 | resizer->resizer_a.input = RESIZER_INPUT_NONE; | ||
1714 | break; | ||
1715 | } | ||
1716 | if (resizer->resizer_a.input != RESIZER_INPUT_NONE) | ||
1717 | return -EBUSY; | ||
1718 | resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; | ||
1719 | break; | ||
1720 | |||
1721 | case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1722 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1723 | resizer->resizer_a.output = RESIZER_OUTPUT_NONE; | ||
1724 | break; | ||
1725 | } | ||
1726 | if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) | ||
1727 | return -EBUSY; | ||
1728 | resizer->resizer_a.output = RESIZER_OUPUT_MEMORY; | ||
1729 | break; | ||
1730 | |||
1731 | default: | ||
1732 | return -EINVAL; | ||
1733 | } | ||
1734 | } else if (&resizer->resizer_b.subdev == sd) { | ||
1735 | switch (local->index | media_entity_type(remote->entity)) { | ||
1736 | case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: | ||
1737 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1738 | resizer->resizer_b.input = RESIZER_INPUT_NONE; | ||
1739 | break; | ||
1740 | } | ||
1741 | if (resizer->resizer_b.input != RESIZER_INPUT_NONE) | ||
1742 | return -EBUSY; | ||
1743 | resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; | ||
1744 | break; | ||
1745 | |||
1746 | case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: | ||
1747 | if (!(flags & MEDIA_LNK_FL_ENABLED)) { | ||
1748 | resizer->resizer_b.output = RESIZER_OUTPUT_NONE; | ||
1749 | break; | ||
1750 | } | ||
1751 | if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) | ||
1752 | return -EBUSY; | ||
1753 | resizer->resizer_b.output = RESIZER_OUPUT_MEMORY; | ||
1754 | break; | ||
1755 | |||
1756 | default: | ||
1757 | return -EINVAL; | ||
1758 | } | ||
1759 | } else { | ||
1760 | return -EINVAL; | ||
1761 | } | ||
1762 | |||
1763 | return 0; | ||
1764 | } | ||
1765 | |||
1766 | static const struct media_entity_operations resizer_media_ops = { | ||
1767 | .link_setup = resizer_link_setup, | ||
1768 | }; | ||
1769 | |||
1770 | /* | ||
1771 | * vpfe_resizer_unregister_entities() - Unregister entity | ||
1772 | * @vpfe_rsz - pointer to resizer subdevice structure. | ||
1773 | */ | ||
1774 | void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) | ||
1775 | { | ||
1776 | /* unregister video devices */ | ||
1777 | vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out); | ||
1778 | vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out); | ||
1779 | |||
1780 | /* cleanup entity */ | ||
1781 | media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity); | ||
1782 | media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity); | ||
1783 | media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity); | ||
1784 | /* unregister subdev */ | ||
1785 | v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev); | ||
1786 | v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev); | ||
1787 | v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev); | ||
1788 | } | ||
1789 | |||
1790 | /* | ||
1791 | * vpfe_resizer_register_entities() - Register entity | ||
1792 | * @resizer - pointer to resizer devive. | ||
1793 | * @vdev: pointer to v4l2 device structure. | ||
1794 | */ | ||
1795 | int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, | ||
1796 | struct v4l2_device *vdev) | ||
1797 | { | ||
1798 | struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); | ||
1799 | unsigned int flags = 0; | ||
1800 | int ret; | ||
1801 | |||
1802 | /* Register the crop resizer subdev */ | ||
1803 | ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev); | ||
1804 | if (ret < 0) { | ||
1805 | pr_err("Failed to register crop resizer as v4l2-subdev\n"); | ||
1806 | return ret; | ||
1807 | } | ||
1808 | /* Register Resizer-A subdev */ | ||
1809 | ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev); | ||
1810 | if (ret < 0) { | ||
1811 | pr_err("Failed to register resizer-a as v4l2-subdev\n"); | ||
1812 | return ret; | ||
1813 | } | ||
1814 | /* Register Resizer-B subdev */ | ||
1815 | ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev); | ||
1816 | if (ret < 0) { | ||
1817 | pr_err("Failed to register resizer-b as v4l2-subdev\n"); | ||
1818 | return ret; | ||
1819 | } | ||
1820 | /* Register video-out device for resizer-a */ | ||
1821 | ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev); | ||
1822 | if (ret) { | ||
1823 | pr_err("Failed to register RSZ-A video-out device\n"); | ||
1824 | goto out_video_out2_register; | ||
1825 | } | ||
1826 | resizer->resizer_a.video_out.vpfe_dev = vpfe_dev; | ||
1827 | |||
1828 | /* Register video-out device for resizer-b */ | ||
1829 | ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev); | ||
1830 | if (ret) { | ||
1831 | pr_err("Failed to register RSZ-B video-out device\n"); | ||
1832 | goto out_video_out2_register; | ||
1833 | } | ||
1834 | resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; | ||
1835 | |||
1836 | /* create link between Resizer Crop----> Resizer A*/ | ||
1837 | ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, | ||
1838 | &resizer->resizer_a.subdev.entity, | ||
1839 | 0, flags); | ||
1840 | if (ret < 0) | ||
1841 | goto out_create_link; | ||
1842 | |||
1843 | /* create link between Resizer Crop----> Resizer B*/ | ||
1844 | ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, | ||
1845 | &resizer->resizer_b.subdev.entity, | ||
1846 | 0, flags); | ||
1847 | if (ret < 0) | ||
1848 | goto out_create_link; | ||
1849 | |||
1850 | /* create link between Resizer A ----> video out */ | ||
1851 | ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, | ||
1852 | &resizer->resizer_a.video_out.video_dev.entity, 0, flags); | ||
1853 | if (ret < 0) | ||
1854 | goto out_create_link; | ||
1855 | |||
1856 | /* create link between Resizer B ----> video out */ | ||
1857 | ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, | ||
1858 | &resizer->resizer_b.video_out.video_dev.entity, 0, flags); | ||
1859 | if (ret < 0) | ||
1860 | goto out_create_link; | ||
1861 | |||
1862 | return 0; | ||
1863 | |||
1864 | out_create_link: | ||
1865 | vpfe_video_unregister(&resizer->resizer_b.video_out); | ||
1866 | out_video_out2_register: | ||
1867 | vpfe_video_unregister(&resizer->resizer_a.video_out); | ||
1868 | media_entity_cleanup(&resizer->crop_resizer.subdev.entity); | ||
1869 | media_entity_cleanup(&resizer->resizer_a.subdev.entity); | ||
1870 | media_entity_cleanup(&resizer->resizer_b.subdev.entity); | ||
1871 | v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev); | ||
1872 | v4l2_device_unregister_subdev(&resizer->resizer_a.subdev); | ||
1873 | v4l2_device_unregister_subdev(&resizer->resizer_b.subdev); | ||
1874 | return ret; | ||
1875 | } | ||
1876 | |||
1877 | /* | ||
1878 | * vpfe_resizer_init() - resizer device initialization. | ||
1879 | * @vpfe_rsz - pointer to resizer device | ||
1880 | * @pdev: platform device pointer. | ||
1881 | */ | ||
1882 | int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, | ||
1883 | struct platform_device *pdev) | ||
1884 | { | ||
1885 | struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev; | ||
1886 | struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0]; | ||
1887 | struct media_entity *me = &sd->entity; | ||
1888 | static resource_size_t res_len; | ||
1889 | struct resource *res; | ||
1890 | int ret; | ||
1891 | |||
1892 | res = platform_get_resource(pdev, IORESOURCE_MEM, 5); | ||
1893 | if (!res) | ||
1894 | return -ENOENT; | ||
1895 | |||
1896 | res_len = resource_size(res); | ||
1897 | res = request_mem_region(res->start, res_len, res->name); | ||
1898 | if (!res) | ||
1899 | return -EBUSY; | ||
1900 | |||
1901 | vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len); | ||
1902 | if (!vpfe_rsz->base_addr) | ||
1903 | return -EBUSY; | ||
1904 | |||
1905 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1906 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1907 | strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); | ||
1908 | sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ | ||
1909 | v4l2_set_subdevdata(sd, vpfe_rsz); | ||
1910 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1911 | |||
1912 | pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1913 | pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1914 | pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; | ||
1915 | |||
1916 | vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE; | ||
1917 | vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; | ||
1918 | vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; | ||
1919 | vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; | ||
1920 | me->ops = &resizer_media_ops; | ||
1921 | ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); | ||
1922 | if (ret) | ||
1923 | return ret; | ||
1924 | |||
1925 | sd = &vpfe_rsz->resizer_a.subdev; | ||
1926 | pads = &vpfe_rsz->resizer_a.pads[0]; | ||
1927 | me = &sd->entity; | ||
1928 | |||
1929 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1930 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1931 | strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); | ||
1932 | sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ | ||
1933 | v4l2_set_subdevdata(sd, vpfe_rsz); | ||
1934 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1935 | |||
1936 | pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1937 | pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1938 | |||
1939 | vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE; | ||
1940 | vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; | ||
1941 | vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; | ||
1942 | me->ops = &resizer_media_ops; | ||
1943 | ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); | ||
1944 | if (ret) | ||
1945 | return ret; | ||
1946 | |||
1947 | sd = &vpfe_rsz->resizer_b.subdev; | ||
1948 | pads = &vpfe_rsz->resizer_b.pads[0]; | ||
1949 | me = &sd->entity; | ||
1950 | |||
1951 | v4l2_subdev_init(sd, &resizer_v4l2_ops); | ||
1952 | sd->internal_ops = &resizer_v4l2_internal_ops; | ||
1953 | strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); | ||
1954 | sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ | ||
1955 | v4l2_set_subdevdata(sd, vpfe_rsz); | ||
1956 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
1957 | |||
1958 | pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; | ||
1959 | pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; | ||
1960 | |||
1961 | vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE; | ||
1962 | vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; | ||
1963 | vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; | ||
1964 | me->ops = &resizer_media_ops; | ||
1965 | ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); | ||
1966 | if (ret) | ||
1967 | return ret; | ||
1968 | |||
1969 | vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops; | ||
1970 | vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1971 | ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A"); | ||
1972 | if (ret) { | ||
1973 | pr_err("Failed to init RSZ video-out device\n"); | ||
1974 | return ret; | ||
1975 | } | ||
1976 | vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops; | ||
1977 | vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1978 | ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B"); | ||
1979 | if (ret) { | ||
1980 | pr_err("Failed to init RSZ video-out2 device\n"); | ||
1981 | return ret; | ||
1982 | } | ||
1983 | memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params)); | ||
1984 | |||
1985 | return 0; | ||
1986 | } | ||
1987 | |||
1988 | void | ||
1989 | vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, | ||
1990 | struct platform_device *pdev) | ||
1991 | { | ||
1992 | struct resource *res; | ||
1993 | |||
1994 | iounmap(vpfe_rsz->base_addr); | ||
1995 | res = platform_get_resource(pdev, IORESOURCE_MEM, 5); | ||
1996 | if (res) | ||
1997 | release_mem_region(res->start, | ||
1998 | res->end - res->start + 1); | ||
1999 | } | ||
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h new file mode 100644 index 000000000000..59a79422b914 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments Inc | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | * | ||
17 | * Contributors: | ||
18 | * Manjunath Hadli <manjunath.hadli@ti.com> | ||
19 | * Prabhakar Lad <prabhakar.lad@ti.com> | ||
20 | */ | ||
21 | |||
22 | #ifndef _DAVINCI_VPFE_DM365_RESIZER_H | ||
23 | #define _DAVINCI_VPFE_DM365_RESIZER_H | ||
24 | |||
25 | enum resizer_oper_mode { | ||
26 | RESIZER_MODE_CONTINIOUS = 0, | ||
27 | RESIZER_MODE_ONE_SHOT = 1, | ||
28 | }; | ||
29 | |||
30 | struct f_div_pass { | ||
31 | unsigned int o_hsz; | ||
32 | unsigned int i_hps; | ||
33 | unsigned int h_phs; | ||
34 | unsigned int src_hps; | ||
35 | unsigned int src_hsz; | ||
36 | }; | ||
37 | |||
38 | #define MAX_PASSES 2 | ||
39 | |||
40 | struct f_div_param { | ||
41 | unsigned char en; | ||
42 | unsigned int num_passes; | ||
43 | struct f_div_pass pass[MAX_PASSES]; | ||
44 | }; | ||
45 | |||
46 | /* Resizer Rescale Parameters*/ | ||
47 | struct resizer_scale_param { | ||
48 | bool h_flip; | ||
49 | bool v_flip; | ||
50 | bool cen; | ||
51 | bool yen; | ||
52 | unsigned short i_vps; | ||
53 | unsigned short i_hps; | ||
54 | unsigned short o_vsz; | ||
55 | unsigned short o_hsz; | ||
56 | unsigned short v_phs_y; | ||
57 | unsigned short v_phs_c; | ||
58 | unsigned short v_dif; | ||
59 | /* resize method - Luminance */ | ||
60 | enum vpfe_rsz_intp_t v_typ_y; | ||
61 | /* resize method - Chrominance */ | ||
62 | enum vpfe_rsz_intp_t v_typ_c; | ||
63 | /* vertical lpf intensity - Luminance */ | ||
64 | unsigned char v_lpf_int_y; | ||
65 | /* vertical lpf intensity - Chrominance */ | ||
66 | unsigned char v_lpf_int_c; | ||
67 | unsigned short h_phs; | ||
68 | unsigned short h_dif; | ||
69 | /* resize method - Luminance */ | ||
70 | enum vpfe_rsz_intp_t h_typ_y; | ||
71 | /* resize method - Chrominance */ | ||
72 | enum vpfe_rsz_intp_t h_typ_c; | ||
73 | /* horizontal lpf intensity - Luminance */ | ||
74 | unsigned char h_lpf_int_y; | ||
75 | /* horizontal lpf intensity - Chrominance */ | ||
76 | unsigned char h_lpf_int_c; | ||
77 | bool dscale_en; | ||
78 | enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; | ||
79 | enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; | ||
80 | /* store the calculated frame division parameter */ | ||
81 | struct f_div_param f_div; | ||
82 | }; | ||
83 | |||
84 | enum resizer_rgb_t { | ||
85 | OUTPUT_32BIT, | ||
86 | OUTPUT_16BIT | ||
87 | }; | ||
88 | |||
89 | enum resizer_rgb_msk_t { | ||
90 | NOMASK = 0, | ||
91 | MASKLAST2 = 1, | ||
92 | }; | ||
93 | |||
94 | /* Resizer RGB Conversion Parameters */ | ||
95 | struct resizer_rgb { | ||
96 | bool rgb_en; | ||
97 | enum resizer_rgb_t rgb_typ; | ||
98 | enum resizer_rgb_msk_t rgb_msk0; | ||
99 | enum resizer_rgb_msk_t rgb_msk1; | ||
100 | unsigned int rgb_alpha_val; | ||
101 | }; | ||
102 | |||
103 | /* Resizer External Memory Parameters */ | ||
104 | struct rsz_ext_mem_param { | ||
105 | unsigned int rsz_sdr_oft_y; | ||
106 | unsigned int rsz_sdr_ptr_s_y; | ||
107 | unsigned int rsz_sdr_ptr_e_y; | ||
108 | unsigned int rsz_sdr_oft_c; | ||
109 | unsigned int rsz_sdr_ptr_s_c; | ||
110 | unsigned int rsz_sdr_ptr_e_c; | ||
111 | /* offset to be added to buffer start when flipping for y/ycbcr */ | ||
112 | unsigned int flip_ofst_y; | ||
113 | /* offset to be added to buffer start when flipping for c */ | ||
114 | unsigned int flip_ofst_c; | ||
115 | /* c offset for YUV 420SP */ | ||
116 | unsigned int c_offset; | ||
117 | /* User Defined Y offset for YUV 420SP or YUV420ILE data */ | ||
118 | unsigned int user_y_ofst; | ||
119 | /* User Defined C offset for YUV 420SP data */ | ||
120 | unsigned int user_c_ofst; | ||
121 | }; | ||
122 | |||
123 | enum rsz_data_source { | ||
124 | IPIPE_DATA, | ||
125 | IPIPEIF_DATA | ||
126 | }; | ||
127 | |||
128 | enum rsz_src_img_fmt { | ||
129 | RSZ_IMG_422, | ||
130 | RSZ_IMG_420 | ||
131 | }; | ||
132 | |||
133 | enum rsz_dpaths_bypass_t { | ||
134 | BYPASS_OFF = 0, | ||
135 | BYPASS_ON = 1, | ||
136 | }; | ||
137 | |||
138 | struct rsz_common_params { | ||
139 | unsigned int vps; | ||
140 | unsigned int vsz; | ||
141 | unsigned int hps; | ||
142 | unsigned int hsz; | ||
143 | /* 420 or 422 */ | ||
144 | enum rsz_src_img_fmt src_img_fmt; | ||
145 | /* Y or C when src_fmt is 420, 0 - y, 1 - c */ | ||
146 | unsigned char y_c; | ||
147 | /* flip raw or ycbcr */ | ||
148 | unsigned char raw_flip; | ||
149 | /* IPIPE or IPIPEIF data */ | ||
150 | enum rsz_data_source source; | ||
151 | enum rsz_dpaths_bypass_t passthrough; | ||
152 | unsigned char yuv_y_min; | ||
153 | unsigned char yuv_y_max; | ||
154 | unsigned char yuv_c_min; | ||
155 | unsigned char yuv_c_max; | ||
156 | bool rsz_seq_crv; | ||
157 | enum vpfe_chr_pos out_chr_pos; | ||
158 | }; | ||
159 | |||
160 | struct resizer_params { | ||
161 | enum resizer_oper_mode oper_mode; | ||
162 | struct rsz_common_params rsz_common; | ||
163 | struct resizer_scale_param rsz_rsc_param[2]; | ||
164 | struct resizer_rgb rsz2rgb[2]; | ||
165 | struct rsz_ext_mem_param ext_mem_param[2]; | ||
166 | bool rsz_en[2]; | ||
167 | struct vpfe_rsz_config_params user_config; | ||
168 | }; | ||
169 | |||
170 | #define ENABLE 1 | ||
171 | #define DISABLE (!ENABLE) | ||
172 | |||
173 | #define RESIZER_CROP_PAD_SINK 0 | ||
174 | #define RESIZER_CROP_PAD_SOURCE 1 | ||
175 | #define RESIZER_CROP_PAD_SOURCE2 2 | ||
176 | |||
177 | #define RESIZER_CROP_PADS_NUM 3 | ||
178 | |||
179 | enum resizer_crop_input_entity { | ||
180 | RESIZER_CROP_INPUT_NONE = 0, | ||
181 | RESIZER_CROP_INPUT_IPIPEIF = 1, | ||
182 | RESIZER_CROP_INPUT_IPIPE = 2, | ||
183 | }; | ||
184 | |||
185 | enum resizer_crop_output_entity { | ||
186 | RESIZER_CROP_OUTPUT_NONE, | ||
187 | RESIZER_A, | ||
188 | RESIZER_B, | ||
189 | }; | ||
190 | |||
191 | struct dm365_crop_resizer_device { | ||
192 | struct v4l2_subdev subdev; | ||
193 | struct media_pad pads[RESIZER_CROP_PADS_NUM]; | ||
194 | struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM]; | ||
195 | enum resizer_crop_input_entity input; | ||
196 | enum resizer_crop_output_entity output; | ||
197 | enum resizer_crop_output_entity output2; | ||
198 | struct vpfe_resizer_device *rsz_device; | ||
199 | }; | ||
200 | |||
201 | #define RESIZER_PAD_SINK 0 | ||
202 | #define RESIZER_PAD_SOURCE 1 | ||
203 | |||
204 | #define RESIZER_PADS_NUM 2 | ||
205 | |||
206 | enum resizer_input_entity { | ||
207 | RESIZER_INPUT_NONE = 0, | ||
208 | RESIZER_INPUT_CROP_RESIZER = 1, | ||
209 | }; | ||
210 | |||
211 | enum resizer_output_entity { | ||
212 | RESIZER_OUTPUT_NONE = 0, | ||
213 | RESIZER_OUPUT_MEMORY = 1, | ||
214 | }; | ||
215 | |||
216 | struct dm365_resizer_device { | ||
217 | struct v4l2_subdev subdev; | ||
218 | struct media_pad pads[RESIZER_PADS_NUM]; | ||
219 | struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; | ||
220 | enum resizer_input_entity input; | ||
221 | enum resizer_output_entity output; | ||
222 | struct vpfe_video_device video_out; | ||
223 | struct vpfe_resizer_device *rsz_device; | ||
224 | }; | ||
225 | |||
226 | struct vpfe_resizer_device { | ||
227 | struct dm365_crop_resizer_device crop_resizer; | ||
228 | struct dm365_resizer_device resizer_a; | ||
229 | struct dm365_resizer_device resizer_b; | ||
230 | struct resizer_params config; | ||
231 | void *__iomem base_addr; | ||
232 | }; | ||
233 | |||
234 | int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, | ||
235 | struct platform_device *pdev); | ||
236 | int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, | ||
237 | struct v4l2_device *v4l2_dev); | ||
238 | void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); | ||
239 | void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, | ||
240 | struct platform_device *pdev); | ||
241 | void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer); | ||
242 | void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer); | ||
243 | |||
244 | #endif /* _DAVINCI_VPFE_DM365_RESIZER_H */ | ||