diff options
author | Sungchun Kang <sungchun.kang@samsung.com> | 2012-07-31 09:44:04 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-09-15 10:06:00 -0400 |
commit | 89069699769aa4108023a80ac953454e1d26af6b (patch) | |
tree | f10e5cc4c55c3f9be368a6407dd50b14f09047a2 /drivers/media/platform | |
parent | 199854a3a8e44d685ede39f9aaba4f73669e1039 (diff) |
[media] gscaler: Add core functionality for the G-Scaler driver
This patch adds the core functionality for the G-Scaler driver.
Signed-off-by: Hynwoong Kim <khw0178.kim@samsung.com>
Signed-off-by: Sungchun Kang <sungchun.kang@samsung.com>
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r-- | drivers/media/platform/exynos-gsc/gsc-core.c | 1253 | ||||
-rw-r--r-- | drivers/media/platform/exynos-gsc/gsc-core.h | 527 |
2 files changed, 1780 insertions, 0 deletions
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c new file mode 100644 index 000000000000..c5c7625e5d59 --- /dev/null +++ b/drivers/media/platform/exynos-gsc/gsc-core.c | |||
@@ -0,0 +1,1253 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com | ||
4 | * | ||
5 | * Samsung EXYNOS5 SoC series G-Scaler driver | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published | ||
9 | * by the Free Software Foundation, either version 2 of the License, | ||
10 | * or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/version.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/bug.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/workqueue.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/of.h> | ||
28 | #include <media/v4l2-ioctl.h> | ||
29 | |||
30 | #include "gsc-core.h" | ||
31 | |||
32 | #define GSC_CLOCK_GATE_NAME "gscl" | ||
33 | |||
34 | static const struct gsc_fmt gsc_formats[] = { | ||
35 | { | ||
36 | .name = "RGB565", | ||
37 | .pixelformat = V4L2_PIX_FMT_RGB565X, | ||
38 | .depth = { 16 }, | ||
39 | .color = GSC_RGB, | ||
40 | .num_planes = 1, | ||
41 | .num_comp = 1, | ||
42 | }, { | ||
43 | .name = "XRGB-8-8-8-8, 32 bpp", | ||
44 | .pixelformat = V4L2_PIX_FMT_RGB32, | ||
45 | .depth = { 32 }, | ||
46 | .color = GSC_RGB, | ||
47 | .num_planes = 1, | ||
48 | .num_comp = 1, | ||
49 | }, { | ||
50 | .name = "YUV 4:2:2 packed, YCbYCr", | ||
51 | .pixelformat = V4L2_PIX_FMT_YUYV, | ||
52 | .depth = { 16 }, | ||
53 | .color = GSC_YUV422, | ||
54 | .yorder = GSC_LSB_Y, | ||
55 | .corder = GSC_CBCR, | ||
56 | .num_planes = 1, | ||
57 | .num_comp = 1, | ||
58 | .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, | ||
59 | }, { | ||
60 | .name = "YUV 4:2:2 packed, CbYCrY", | ||
61 | .pixelformat = V4L2_PIX_FMT_UYVY, | ||
62 | .depth = { 16 }, | ||
63 | .color = GSC_YUV422, | ||
64 | .yorder = GSC_LSB_C, | ||
65 | .corder = GSC_CBCR, | ||
66 | .num_planes = 1, | ||
67 | .num_comp = 1, | ||
68 | .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, | ||
69 | }, { | ||
70 | .name = "YUV 4:2:2 packed, CrYCbY", | ||
71 | .pixelformat = V4L2_PIX_FMT_VYUY, | ||
72 | .depth = { 16 }, | ||
73 | .color = GSC_YUV422, | ||
74 | .yorder = GSC_LSB_C, | ||
75 | .corder = GSC_CRCB, | ||
76 | .num_planes = 1, | ||
77 | .num_comp = 1, | ||
78 | .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, | ||
79 | }, { | ||
80 | .name = "YUV 4:2:2 packed, YCrYCb", | ||
81 | .pixelformat = V4L2_PIX_FMT_YVYU, | ||
82 | .depth = { 16 }, | ||
83 | .color = GSC_YUV422, | ||
84 | .yorder = GSC_LSB_Y, | ||
85 | .corder = GSC_CRCB, | ||
86 | .num_planes = 1, | ||
87 | .num_comp = 1, | ||
88 | .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, | ||
89 | }, { | ||
90 | .name = "YUV 4:4:4 planar, YCbYCr", | ||
91 | .pixelformat = V4L2_PIX_FMT_YUV32, | ||
92 | .depth = { 32 }, | ||
93 | .color = GSC_YUV444, | ||
94 | .yorder = GSC_LSB_Y, | ||
95 | .corder = GSC_CBCR, | ||
96 | .num_planes = 1, | ||
97 | .num_comp = 1, | ||
98 | }, { | ||
99 | .name = "YUV 4:2:2 planar, Y/Cb/Cr", | ||
100 | .pixelformat = V4L2_PIX_FMT_YUV422P, | ||
101 | .depth = { 16 }, | ||
102 | .color = GSC_YUV422, | ||
103 | .yorder = GSC_LSB_Y, | ||
104 | .corder = GSC_CBCR, | ||
105 | .num_planes = 1, | ||
106 | .num_comp = 3, | ||
107 | }, { | ||
108 | .name = "YUV 4:2:2 planar, Y/CbCr", | ||
109 | .pixelformat = V4L2_PIX_FMT_NV16, | ||
110 | .depth = { 16 }, | ||
111 | .color = GSC_YUV422, | ||
112 | .yorder = GSC_LSB_Y, | ||
113 | .corder = GSC_CBCR, | ||
114 | .num_planes = 1, | ||
115 | .num_comp = 2, | ||
116 | }, { | ||
117 | .name = "YUV 4:2:2 planar, Y/CrCb", | ||
118 | .pixelformat = V4L2_PIX_FMT_NV61, | ||
119 | .depth = { 16 }, | ||
120 | .color = GSC_YUV422, | ||
121 | .yorder = GSC_LSB_Y, | ||
122 | .corder = GSC_CRCB, | ||
123 | .num_planes = 1, | ||
124 | .num_comp = 2, | ||
125 | }, { | ||
126 | .name = "YUV 4:2:0 planar, YCbCr", | ||
127 | .pixelformat = V4L2_PIX_FMT_YUV420, | ||
128 | .depth = { 12 }, | ||
129 | .color = GSC_YUV420, | ||
130 | .yorder = GSC_LSB_Y, | ||
131 | .corder = GSC_CBCR, | ||
132 | .num_planes = 1, | ||
133 | .num_comp = 3, | ||
134 | }, { | ||
135 | .name = "YUV 4:2:0 planar, YCrCb", | ||
136 | .pixelformat = V4L2_PIX_FMT_YVU420, | ||
137 | .depth = { 12 }, | ||
138 | .color = GSC_YUV420, | ||
139 | .yorder = GSC_LSB_Y, | ||
140 | .corder = GSC_CRCB, | ||
141 | .num_planes = 1, | ||
142 | .num_comp = 3, | ||
143 | |||
144 | }, { | ||
145 | .name = "YUV 4:2:0 planar, Y/CbCr", | ||
146 | .pixelformat = V4L2_PIX_FMT_NV12, | ||
147 | .depth = { 12 }, | ||
148 | .color = GSC_YUV420, | ||
149 | .yorder = GSC_LSB_Y, | ||
150 | .corder = GSC_CBCR, | ||
151 | .num_planes = 1, | ||
152 | .num_comp = 2, | ||
153 | }, { | ||
154 | .name = "YUV 4:2:0 planar, Y/CrCb", | ||
155 | .pixelformat = V4L2_PIX_FMT_NV21, | ||
156 | .depth = { 12 }, | ||
157 | .color = GSC_YUV420, | ||
158 | .yorder = GSC_LSB_Y, | ||
159 | .corder = GSC_CRCB, | ||
160 | .num_planes = 1, | ||
161 | .num_comp = 2, | ||
162 | }, { | ||
163 | .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr", | ||
164 | .pixelformat = V4L2_PIX_FMT_NV12M, | ||
165 | .depth = { 8, 4 }, | ||
166 | .color = GSC_YUV420, | ||
167 | .yorder = GSC_LSB_Y, | ||
168 | .corder = GSC_CBCR, | ||
169 | .num_planes = 2, | ||
170 | .num_comp = 2, | ||
171 | }, { | ||
172 | .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr", | ||
173 | .pixelformat = V4L2_PIX_FMT_YUV420M, | ||
174 | .depth = { 8, 2, 2 }, | ||
175 | .color = GSC_YUV420, | ||
176 | .yorder = GSC_LSB_Y, | ||
177 | .corder = GSC_CBCR, | ||
178 | .num_planes = 3, | ||
179 | .num_comp = 3, | ||
180 | }, { | ||
181 | .name = "YUV 4:2:0 non-contig. 3p, Y/Cr/Cb", | ||
182 | .pixelformat = V4L2_PIX_FMT_YVU420M, | ||
183 | .depth = { 8, 2, 2 }, | ||
184 | .color = GSC_YUV420, | ||
185 | .yorder = GSC_LSB_Y, | ||
186 | .corder = GSC_CRCB, | ||
187 | .num_planes = 3, | ||
188 | .num_comp = 3, | ||
189 | } | ||
190 | }; | ||
191 | |||
192 | const struct gsc_fmt *get_format(int index) | ||
193 | { | ||
194 | if (index >= ARRAY_SIZE(gsc_formats)) | ||
195 | return NULL; | ||
196 | |||
197 | return (struct gsc_fmt *)&gsc_formats[index]; | ||
198 | } | ||
199 | |||
200 | const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index) | ||
201 | { | ||
202 | const struct gsc_fmt *fmt, *def_fmt = NULL; | ||
203 | unsigned int i; | ||
204 | |||
205 | if (index >= ARRAY_SIZE(gsc_formats)) | ||
206 | return NULL; | ||
207 | |||
208 | for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) { | ||
209 | fmt = get_format(i); | ||
210 | if (pixelformat && fmt->pixelformat == *pixelformat) | ||
211 | return fmt; | ||
212 | if (mbus_code && fmt->mbus_code == *mbus_code) | ||
213 | return fmt; | ||
214 | if (index == i) | ||
215 | def_fmt = fmt; | ||
216 | } | ||
217 | return def_fmt; | ||
218 | |||
219 | } | ||
220 | |||
221 | void gsc_set_frame_size(struct gsc_frame *frame, int width, int height) | ||
222 | { | ||
223 | frame->f_width = width; | ||
224 | frame->f_height = height; | ||
225 | frame->crop.width = width; | ||
226 | frame->crop.height = height; | ||
227 | frame->crop.left = 0; | ||
228 | frame->crop.top = 0; | ||
229 | } | ||
230 | |||
231 | int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, | ||
232 | u32 *ratio) | ||
233 | { | ||
234 | if ((dst > src) || (dst >= src / var->poly_sc_down_max)) { | ||
235 | *ratio = 1; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) { | ||
240 | pr_err("Exceeded maximum downscaling ratio (1/16))"); | ||
241 | return -EINVAL; | ||
242 | } | ||
243 | |||
244 | *ratio = (dst > (src / 8)) ? 2 : 4; | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh) | ||
250 | { | ||
251 | if (hratio == 4 && vratio == 4) | ||
252 | *sh = 4; | ||
253 | else if ((hratio == 4 && vratio == 2) || | ||
254 | (hratio == 2 && vratio == 4)) | ||
255 | *sh = 3; | ||
256 | else if ((hratio == 4 && vratio == 1) || | ||
257 | (hratio == 1 && vratio == 4) || | ||
258 | (hratio == 2 && vratio == 2)) | ||
259 | *sh = 2; | ||
260 | else if (hratio == 1 && vratio == 1) | ||
261 | *sh = 0; | ||
262 | else | ||
263 | *sh = 1; | ||
264 | } | ||
265 | |||
266 | void gsc_check_src_scale_info(struct gsc_variant *var, | ||
267 | struct gsc_frame *s_frame, u32 *wratio, | ||
268 | u32 tx, u32 ty, u32 *hratio) | ||
269 | { | ||
270 | int remainder = 0, walign, halign; | ||
271 | |||
272 | if (is_yuv420(s_frame->fmt->color)) { | ||
273 | walign = GSC_SC_ALIGN_4; | ||
274 | halign = GSC_SC_ALIGN_4; | ||
275 | } else if (is_yuv422(s_frame->fmt->color)) { | ||
276 | walign = GSC_SC_ALIGN_4; | ||
277 | halign = GSC_SC_ALIGN_2; | ||
278 | } else { | ||
279 | walign = GSC_SC_ALIGN_2; | ||
280 | halign = GSC_SC_ALIGN_2; | ||
281 | } | ||
282 | |||
283 | remainder = s_frame->crop.width % (*wratio * walign); | ||
284 | if (remainder) { | ||
285 | s_frame->crop.width -= remainder; | ||
286 | gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio); | ||
287 | pr_info("cropped src width size is recalculated from %d to %d", | ||
288 | s_frame->crop.width + remainder, s_frame->crop.width); | ||
289 | } | ||
290 | |||
291 | remainder = s_frame->crop.height % (*hratio * halign); | ||
292 | if (remainder) { | ||
293 | s_frame->crop.height -= remainder; | ||
294 | gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio); | ||
295 | pr_info("cropped src height size is recalculated from %d to %d", | ||
296 | s_frame->crop.height + remainder, s_frame->crop.height); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f) | ||
301 | { | ||
302 | const struct gsc_fmt *fmt; | ||
303 | |||
304 | fmt = find_fmt(NULL, NULL, f->index); | ||
305 | if (!fmt) | ||
306 | return -EINVAL; | ||
307 | |||
308 | strlcpy(f->description, fmt->name, sizeof(f->description)); | ||
309 | f->pixelformat = fmt->pixelformat; | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | u32 get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index) | ||
315 | { | ||
316 | if (frm->addr.y == addr) { | ||
317 | *index = 0; | ||
318 | return frm->addr.y; | ||
319 | } else if (frm->addr.cb == addr) { | ||
320 | *index = 1; | ||
321 | return frm->addr.cb; | ||
322 | } else if (frm->addr.cr == addr) { | ||
323 | *index = 2; | ||
324 | return frm->addr.cr; | ||
325 | } else { | ||
326 | pr_err("Plane address is wrong"); | ||
327 | return -EINVAL; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm) | ||
332 | { | ||
333 | u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len; | ||
334 | f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0; | ||
335 | |||
336 | f_chk_addr = frm->addr.y; | ||
337 | f_chk_len = frm->payload[0]; | ||
338 | if (frm->fmt->num_planes == 2) { | ||
339 | s_chk_addr = frm->addr.cb; | ||
340 | s_chk_len = frm->payload[1]; | ||
341 | } else if (frm->fmt->num_planes == 3) { | ||
342 | u32 low_addr, low_plane, mid_addr, mid_plane; | ||
343 | u32 high_addr, high_plane; | ||
344 | u32 t_min, t_max; | ||
345 | |||
346 | t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr); | ||
347 | low_addr = get_plane_info(frm, t_min, &low_plane); | ||
348 | t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr); | ||
349 | high_addr = get_plane_info(frm, t_max, &high_plane); | ||
350 | |||
351 | mid_plane = 3 - (low_plane + high_plane); | ||
352 | if (mid_plane == 0) | ||
353 | mid_addr = frm->addr.y; | ||
354 | else if (mid_plane == 1) | ||
355 | mid_addr = frm->addr.cb; | ||
356 | else if (mid_plane == 2) | ||
357 | mid_addr = frm->addr.cr; | ||
358 | else | ||
359 | return; | ||
360 | |||
361 | f_chk_addr = low_addr; | ||
362 | if (mid_addr + frm->payload[mid_plane] - low_addr > | ||
363 | high_addr + frm->payload[high_plane] - mid_addr) { | ||
364 | f_chk_len = frm->payload[low_plane]; | ||
365 | s_chk_addr = mid_addr; | ||
366 | s_chk_len = high_addr + | ||
367 | frm->payload[high_plane] - mid_addr; | ||
368 | } else { | ||
369 | f_chk_len = mid_addr + | ||
370 | frm->payload[mid_plane] - low_addr; | ||
371 | s_chk_addr = high_addr; | ||
372 | s_chk_len = frm->payload[high_plane]; | ||
373 | } | ||
374 | } | ||
375 | pr_debug("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n", | ||
376 | f_chk_addr, f_chk_len, s_chk_addr, s_chk_len); | ||
377 | } | ||
378 | |||
379 | int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) | ||
380 | { | ||
381 | struct gsc_dev *gsc = ctx->gsc_dev; | ||
382 | struct gsc_variant *variant = gsc->variant; | ||
383 | struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; | ||
384 | const struct gsc_fmt *fmt; | ||
385 | u32 max_w, max_h, mod_x, mod_y; | ||
386 | u32 min_w, min_h, tmp_w, tmp_h; | ||
387 | int i; | ||
388 | |||
389 | pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height); | ||
390 | |||
391 | fmt = find_fmt(&pix_mp->pixelformat, NULL, 0); | ||
392 | if (!fmt) { | ||
393 | pr_err("pixelformat format (0x%X) invalid\n", | ||
394 | pix_mp->pixelformat); | ||
395 | return -EINVAL; | ||
396 | } | ||
397 | |||
398 | if (pix_mp->field == V4L2_FIELD_ANY) | ||
399 | pix_mp->field = V4L2_FIELD_NONE; | ||
400 | else if (pix_mp->field != V4L2_FIELD_NONE) { | ||
401 | pr_err("Not supported field order(%d)\n", pix_mp->field); | ||
402 | return -EINVAL; | ||
403 | } | ||
404 | |||
405 | max_w = variant->pix_max->target_rot_dis_w; | ||
406 | max_h = variant->pix_max->target_rot_dis_h; | ||
407 | |||
408 | mod_x = ffs(variant->pix_align->org_w) - 1; | ||
409 | if (is_yuv420(fmt->color)) | ||
410 | mod_y = ffs(variant->pix_align->org_h) - 1; | ||
411 | else | ||
412 | mod_y = ffs(variant->pix_align->org_h) - 2; | ||
413 | |||
414 | if (V4L2_TYPE_IS_OUTPUT(f->type)) { | ||
415 | min_w = variant->pix_min->org_w; | ||
416 | min_h = variant->pix_min->org_h; | ||
417 | } else { | ||
418 | min_w = variant->pix_min->target_rot_dis_w; | ||
419 | min_h = variant->pix_min->target_rot_dis_h; | ||
420 | } | ||
421 | |||
422 | pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d", | ||
423 | mod_x, mod_y, max_w, max_h); | ||
424 | |||
425 | /* To check if image size is modified to adjust parameter against | ||
426 | hardware abilities */ | ||
427 | tmp_w = pix_mp->width; | ||
428 | tmp_h = pix_mp->height; | ||
429 | |||
430 | v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x, | ||
431 | &pix_mp->height, min_h, max_h, mod_y, 0); | ||
432 | if (tmp_w != pix_mp->width || tmp_h != pix_mp->height) | ||
433 | pr_info("Image size has been modified from %dx%d to %dx%d", | ||
434 | tmp_w, tmp_h, pix_mp->width, pix_mp->height); | ||
435 | |||
436 | pix_mp->num_planes = fmt->num_planes; | ||
437 | |||
438 | if (pix_mp->width >= 1280) /* HD */ | ||
439 | pix_mp->colorspace = V4L2_COLORSPACE_REC709; | ||
440 | else /* SD */ | ||
441 | pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M; | ||
442 | |||
443 | |||
444 | for (i = 0; i < pix_mp->num_planes; ++i) { | ||
445 | int bpl = (pix_mp->width * fmt->depth[i]) >> 3; | ||
446 | pix_mp->plane_fmt[i].bytesperline = bpl; | ||
447 | pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height; | ||
448 | |||
449 | pr_debug("[%d]: bpl: %d, sizeimage: %d", | ||
450 | i, bpl, pix_mp->plane_fmt[i].sizeimage); | ||
451 | } | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) | ||
457 | { | ||
458 | struct gsc_frame *frame; | ||
459 | struct v4l2_pix_format_mplane *pix_mp; | ||
460 | int i; | ||
461 | |||
462 | frame = ctx_get_frame(ctx, f->type); | ||
463 | if (IS_ERR(frame)) | ||
464 | return PTR_ERR(frame); | ||
465 | |||
466 | pix_mp = &f->fmt.pix_mp; | ||
467 | |||
468 | pix_mp->width = frame->f_width; | ||
469 | pix_mp->height = frame->f_height; | ||
470 | pix_mp->field = V4L2_FIELD_NONE; | ||
471 | pix_mp->pixelformat = frame->fmt->pixelformat; | ||
472 | pix_mp->colorspace = V4L2_COLORSPACE_REC709; | ||
473 | pix_mp->num_planes = frame->fmt->num_planes; | ||
474 | |||
475 | for (i = 0; i < pix_mp->num_planes; ++i) { | ||
476 | pix_mp->plane_fmt[i].bytesperline = (frame->f_width * | ||
477 | frame->fmt->depth[i]) / 8; | ||
478 | pix_mp->plane_fmt[i].sizeimage = | ||
479 | pix_mp->plane_fmt[i].bytesperline * frame->f_height; | ||
480 | } | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h) | ||
486 | { | ||
487 | if (tmp_w != *w || tmp_h != *h) { | ||
488 | pr_info("Cropped size has been modified from %dx%d to %dx%d", | ||
489 | *w, *h, tmp_w, tmp_h); | ||
490 | *w = tmp_w; | ||
491 | *h = tmp_h; | ||
492 | } | ||
493 | } | ||
494 | |||
495 | int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr) | ||
496 | { | ||
497 | struct gsc_frame *frame; | ||
498 | |||
499 | frame = ctx_get_frame(ctx, cr->type); | ||
500 | if (IS_ERR(frame)) | ||
501 | return PTR_ERR(frame); | ||
502 | |||
503 | cr->c = frame->crop; | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr) | ||
509 | { | ||
510 | struct gsc_frame *f; | ||
511 | struct gsc_dev *gsc = ctx->gsc_dev; | ||
512 | struct gsc_variant *variant = gsc->variant; | ||
513 | u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h; | ||
514 | u32 min_w, min_h, max_w, max_h; | ||
515 | |||
516 | if (cr->c.top < 0 || cr->c.left < 0) { | ||
517 | pr_err("doesn't support negative values for top & left\n"); | ||
518 | return -EINVAL; | ||
519 | } | ||
520 | pr_debug("user put w: %d, h: %d", cr->c.width, cr->c.height); | ||
521 | |||
522 | if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
523 | f = &ctx->d_frame; | ||
524 | else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) | ||
525 | f = &ctx->s_frame; | ||
526 | else | ||
527 | return -EINVAL; | ||
528 | |||
529 | max_w = f->f_width; | ||
530 | max_h = f->f_height; | ||
531 | tmp_w = cr->c.width; | ||
532 | tmp_h = cr->c.height; | ||
533 | |||
534 | if (V4L2_TYPE_IS_OUTPUT(cr->type)) { | ||
535 | if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) || | ||
536 | is_rgb(f->fmt->color)) | ||
537 | min_w = 32; | ||
538 | else | ||
539 | min_w = 64; | ||
540 | if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) || | ||
541 | is_yuv420(f->fmt->color)) | ||
542 | min_h = 32; | ||
543 | else | ||
544 | min_h = 16; | ||
545 | } else { | ||
546 | if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) | ||
547 | mod_x = ffs(variant->pix_align->target_w) - 1; | ||
548 | if (is_yuv420(f->fmt->color)) | ||
549 | mod_y = ffs(variant->pix_align->target_h) - 1; | ||
550 | if (ctx->gsc_ctrls.rotate->val == 90 || | ||
551 | ctx->gsc_ctrls.rotate->val == 270) { | ||
552 | max_w = f->f_height; | ||
553 | max_h = f->f_width; | ||
554 | min_w = variant->pix_min->target_rot_en_w; | ||
555 | min_h = variant->pix_min->target_rot_en_h; | ||
556 | tmp_w = cr->c.height; | ||
557 | tmp_h = cr->c.width; | ||
558 | } else { | ||
559 | min_w = variant->pix_min->target_rot_dis_w; | ||
560 | min_h = variant->pix_min->target_rot_dis_h; | ||
561 | } | ||
562 | } | ||
563 | pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d", | ||
564 | mod_x, mod_y, min_w, min_h); | ||
565 | pr_debug("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h); | ||
566 | |||
567 | v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, | ||
568 | &tmp_h, min_h, max_h, mod_y, 0); | ||
569 | |||
570 | if (!V4L2_TYPE_IS_OUTPUT(cr->type) && | ||
571 | (ctx->gsc_ctrls.rotate->val == 90 || | ||
572 | ctx->gsc_ctrls.rotate->val == 270)) | ||
573 | gsc_check_crop_change(tmp_h, tmp_w, | ||
574 | &cr->c.width, &cr->c.height); | ||
575 | else | ||
576 | gsc_check_crop_change(tmp_w, tmp_h, | ||
577 | &cr->c.width, &cr->c.height); | ||
578 | |||
579 | |||
580 | /* adjust left/top if cropping rectangle is out of bounds */ | ||
581 | /* Need to add code to algin left value with 2's multiple */ | ||
582 | if (cr->c.left + tmp_w > max_w) | ||
583 | cr->c.left = max_w - tmp_w; | ||
584 | if (cr->c.top + tmp_h > max_h) | ||
585 | cr->c.top = max_h - tmp_h; | ||
586 | |||
587 | if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) && | ||
588 | cr->c.left & 1) | ||
589 | cr->c.left -= 1; | ||
590 | |||
591 | pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", | ||
592 | cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h); | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw, | ||
598 | int dh, int rot, int out_path) | ||
599 | { | ||
600 | int tmp_w, tmp_h, sc_down_max; | ||
601 | |||
602 | if (out_path == GSC_DMA) | ||
603 | sc_down_max = var->sc_down_max; | ||
604 | else | ||
605 | sc_down_max = var->local_sc_down; | ||
606 | |||
607 | if (rot == 90 || rot == 270) { | ||
608 | tmp_w = dh; | ||
609 | tmp_h = dw; | ||
610 | } else { | ||
611 | tmp_w = dw; | ||
612 | tmp_h = dh; | ||
613 | } | ||
614 | |||
615 | if ((sw / tmp_w) > sc_down_max || | ||
616 | (sh / tmp_h) > sc_down_max || | ||
617 | (tmp_w / sw) > var->sc_up_max || | ||
618 | (tmp_h / sh) > var->sc_up_max) | ||
619 | return -EINVAL; | ||
620 | |||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | int gsc_set_scaler_info(struct gsc_ctx *ctx) | ||
625 | { | ||
626 | struct gsc_scaler *sc = &ctx->scaler; | ||
627 | struct gsc_frame *s_frame = &ctx->s_frame; | ||
628 | struct gsc_frame *d_frame = &ctx->d_frame; | ||
629 | struct gsc_variant *variant = ctx->gsc_dev->variant; | ||
630 | struct device *dev = &ctx->gsc_dev->pdev->dev; | ||
631 | int tx, ty; | ||
632 | int ret; | ||
633 | |||
634 | ret = gsc_check_scaler_ratio(variant, s_frame->crop.width, | ||
635 | s_frame->crop.height, d_frame->crop.width, d_frame->crop.height, | ||
636 | ctx->gsc_ctrls.rotate->val, ctx->out_path); | ||
637 | if (ret) { | ||
638 | pr_err("out of scaler range"); | ||
639 | return ret; | ||
640 | } | ||
641 | |||
642 | if (ctx->gsc_ctrls.rotate->val == 90 || | ||
643 | ctx->gsc_ctrls.rotate->val == 270) { | ||
644 | ty = d_frame->crop.width; | ||
645 | tx = d_frame->crop.height; | ||
646 | } else { | ||
647 | tx = d_frame->crop.width; | ||
648 | ty = d_frame->crop.height; | ||
649 | } | ||
650 | |||
651 | if (tx <= 0 || ty <= 0) { | ||
652 | dev_err(dev, "Invalid target size: %dx%d", tx, ty); | ||
653 | return -EINVAL; | ||
654 | } | ||
655 | |||
656 | ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width, | ||
657 | tx, &sc->pre_hratio); | ||
658 | if (ret) { | ||
659 | pr_err("Horizontal scale ratio is out of range"); | ||
660 | return ret; | ||
661 | } | ||
662 | |||
663 | ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height, | ||
664 | ty, &sc->pre_vratio); | ||
665 | if (ret) { | ||
666 | pr_err("Vertical scale ratio is out of range"); | ||
667 | return ret; | ||
668 | } | ||
669 | |||
670 | gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio, | ||
671 | tx, ty, &sc->pre_vratio); | ||
672 | |||
673 | gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio, | ||
674 | &sc->pre_shfactor); | ||
675 | |||
676 | sc->main_hratio = (s_frame->crop.width << 16) / tx; | ||
677 | sc->main_vratio = (s_frame->crop.height << 16) / ty; | ||
678 | |||
679 | pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d", | ||
680 | s_frame->crop.width, s_frame->crop.height, tx, ty); | ||
681 | pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d", | ||
682 | sc->pre_shfactor, sc->pre_hratio); | ||
683 | pr_debug("pre_v :%d, main_h : %d, main_v : %d", | ||
684 | sc->pre_vratio, sc->main_hratio, sc->main_vratio); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl) | ||
690 | { | ||
691 | struct gsc_dev *gsc = ctx->gsc_dev; | ||
692 | struct gsc_variant *variant = gsc->variant; | ||
693 | unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT; | ||
694 | int ret = 0; | ||
695 | |||
696 | if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) | ||
697 | return 0; | ||
698 | |||
699 | switch (ctrl->id) { | ||
700 | case V4L2_CID_HFLIP: | ||
701 | ctx->hflip = ctrl->val; | ||
702 | break; | ||
703 | |||
704 | case V4L2_CID_VFLIP: | ||
705 | ctx->vflip = ctrl->val; | ||
706 | break; | ||
707 | |||
708 | case V4L2_CID_ROTATE: | ||
709 | if ((ctx->state & flags) == flags) { | ||
710 | ret = gsc_check_scaler_ratio(variant, | ||
711 | ctx->s_frame.crop.width, | ||
712 | ctx->s_frame.crop.height, | ||
713 | ctx->d_frame.crop.width, | ||
714 | ctx->d_frame.crop.height, | ||
715 | ctx->gsc_ctrls.rotate->val, | ||
716 | ctx->out_path); | ||
717 | |||
718 | if (ret) | ||
719 | return -EINVAL; | ||
720 | } | ||
721 | |||
722 | ctx->rotation = ctrl->val; | ||
723 | break; | ||
724 | |||
725 | case V4L2_CID_ALPHA_COMPONENT: | ||
726 | ctx->d_frame.alpha = ctrl->val; | ||
727 | break; | ||
728 | } | ||
729 | |||
730 | ctx->state |= GSC_PARAMS; | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int gsc_s_ctrl(struct v4l2_ctrl *ctrl) | ||
735 | { | ||
736 | struct gsc_ctx *ctx = ctrl_to_ctx(ctrl); | ||
737 | unsigned long flags; | ||
738 | int ret; | ||
739 | |||
740 | spin_lock_irqsave(&ctx->gsc_dev->slock, flags); | ||
741 | ret = __gsc_s_ctrl(ctx, ctrl); | ||
742 | spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); | ||
743 | |||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | const struct v4l2_ctrl_ops gsc_ctrl_ops = { | ||
748 | .s_ctrl = gsc_s_ctrl, | ||
749 | }; | ||
750 | |||
751 | int gsc_ctrls_create(struct gsc_ctx *ctx) | ||
752 | { | ||
753 | if (ctx->ctrls_rdy) { | ||
754 | pr_err("Control handler of this context was created already"); | ||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM); | ||
759 | |||
760 | ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, | ||
761 | &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0); | ||
762 | ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, | ||
763 | &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
764 | ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, | ||
765 | &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
766 | ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler, | ||
767 | &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); | ||
768 | |||
769 | ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; | ||
770 | |||
771 | if (ctx->ctrl_handler.error) { | ||
772 | int err = ctx->ctrl_handler.error; | ||
773 | v4l2_ctrl_handler_free(&ctx->ctrl_handler); | ||
774 | pr_err("Failed to create G-Scaler control handlers"); | ||
775 | return err; | ||
776 | } | ||
777 | |||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | void gsc_ctrls_delete(struct gsc_ctx *ctx) | ||
782 | { | ||
783 | if (ctx->ctrls_rdy) { | ||
784 | v4l2_ctrl_handler_free(&ctx->ctrl_handler); | ||
785 | ctx->ctrls_rdy = false; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | /* The color format (num_comp, num_planes) must be already configured. */ | ||
790 | int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, | ||
791 | struct gsc_frame *frame, struct gsc_addr *addr) | ||
792 | { | ||
793 | int ret = 0; | ||
794 | u32 pix_size; | ||
795 | |||
796 | if ((vb == NULL) || (frame == NULL)) | ||
797 | return -EINVAL; | ||
798 | |||
799 | pix_size = frame->f_width * frame->f_height; | ||
800 | |||
801 | pr_debug("num_planes= %d, num_comp= %d, pix_size= %d", | ||
802 | frame->fmt->num_planes, frame->fmt->num_comp, pix_size); | ||
803 | |||
804 | addr->y = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
805 | |||
806 | if (frame->fmt->num_planes == 1) { | ||
807 | switch (frame->fmt->num_comp) { | ||
808 | case 1: | ||
809 | addr->cb = 0; | ||
810 | addr->cr = 0; | ||
811 | break; | ||
812 | case 2: | ||
813 | /* decompose Y into Y/Cb */ | ||
814 | addr->cb = (dma_addr_t)(addr->y + pix_size); | ||
815 | addr->cr = 0; | ||
816 | break; | ||
817 | case 3: | ||
818 | /* decompose Y into Y/Cb/Cr */ | ||
819 | addr->cb = (dma_addr_t)(addr->y + pix_size); | ||
820 | if (GSC_YUV420 == frame->fmt->color) | ||
821 | addr->cr = (dma_addr_t)(addr->cb | ||
822 | + (pix_size >> 2)); | ||
823 | else /* 422 */ | ||
824 | addr->cr = (dma_addr_t)(addr->cb | ||
825 | + (pix_size >> 1)); | ||
826 | break; | ||
827 | default: | ||
828 | pr_err("Invalid the number of color planes"); | ||
829 | return -EINVAL; | ||
830 | } | ||
831 | } else { | ||
832 | if (frame->fmt->num_planes >= 2) | ||
833 | addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); | ||
834 | |||
835 | if (frame->fmt->num_planes == 3) | ||
836 | addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); | ||
837 | } | ||
838 | |||
839 | if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) || | ||
840 | (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) || | ||
841 | (frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) || | ||
842 | (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) || | ||
843 | (frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) || | ||
844 | (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) | ||
845 | swap(addr->cb, addr->cr); | ||
846 | |||
847 | pr_debug("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", | ||
848 | addr->y, addr->cb, addr->cr, ret); | ||
849 | |||
850 | return ret; | ||
851 | } | ||
852 | |||
853 | static irqreturn_t gsc_irq_handler(int irq, void *priv) | ||
854 | { | ||
855 | struct gsc_dev *gsc = priv; | ||
856 | struct gsc_ctx *ctx; | ||
857 | int gsc_irq; | ||
858 | |||
859 | gsc_irq = gsc_hw_get_irq_status(gsc); | ||
860 | gsc_hw_clear_irq(gsc, gsc_irq); | ||
861 | |||
862 | if (gsc_irq == GSC_IRQ_OVERRUN) { | ||
863 | pr_err("Local path input over-run interrupt has occurred!\n"); | ||
864 | return IRQ_HANDLED; | ||
865 | } | ||
866 | |||
867 | spin_lock(&gsc->slock); | ||
868 | |||
869 | if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) { | ||
870 | |||
871 | gsc_hw_enable_control(gsc, false); | ||
872 | |||
873 | if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) { | ||
874 | set_bit(ST_M2M_SUSPENDED, &gsc->state); | ||
875 | wake_up(&gsc->irq_queue); | ||
876 | goto isr_unlock; | ||
877 | } | ||
878 | ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); | ||
879 | |||
880 | if (!ctx || !ctx->m2m_ctx) | ||
881 | goto isr_unlock; | ||
882 | |||
883 | spin_unlock(&gsc->slock); | ||
884 | gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); | ||
885 | |||
886 | /* wake_up job_abort, stop_streaming */ | ||
887 | if (ctx->state & GSC_CTX_STOP_REQ) { | ||
888 | ctx->state &= ~GSC_CTX_STOP_REQ; | ||
889 | wake_up(&gsc->irq_queue); | ||
890 | } | ||
891 | return IRQ_HANDLED; | ||
892 | } | ||
893 | |||
894 | isr_unlock: | ||
895 | spin_unlock(&gsc->slock); | ||
896 | return IRQ_HANDLED; | ||
897 | } | ||
898 | |||
899 | static struct gsc_pix_max gsc_v_100_max = { | ||
900 | .org_scaler_bypass_w = 8192, | ||
901 | .org_scaler_bypass_h = 8192, | ||
902 | .org_scaler_input_w = 4800, | ||
903 | .org_scaler_input_h = 3344, | ||
904 | .real_rot_dis_w = 4800, | ||
905 | .real_rot_dis_h = 3344, | ||
906 | .real_rot_en_w = 2047, | ||
907 | .real_rot_en_h = 2047, | ||
908 | .target_rot_dis_w = 4800, | ||
909 | .target_rot_dis_h = 3344, | ||
910 | .target_rot_en_w = 2016, | ||
911 | .target_rot_en_h = 2016, | ||
912 | }; | ||
913 | |||
914 | static struct gsc_pix_min gsc_v_100_min = { | ||
915 | .org_w = 64, | ||
916 | .org_h = 32, | ||
917 | .real_w = 64, | ||
918 | .real_h = 32, | ||
919 | .target_rot_dis_w = 64, | ||
920 | .target_rot_dis_h = 32, | ||
921 | .target_rot_en_w = 32, | ||
922 | .target_rot_en_h = 16, | ||
923 | }; | ||
924 | |||
925 | static struct gsc_pix_align gsc_v_100_align = { | ||
926 | .org_h = 16, | ||
927 | .org_w = 16, /* yuv420 : 16, others : 8 */ | ||
928 | .offset_h = 2, /* yuv420/422 : 2, others : 1 */ | ||
929 | .real_w = 16, /* yuv420/422 : 4~16, others : 2~8 */ | ||
930 | .real_h = 16, /* yuv420 : 4~16, others : 1 */ | ||
931 | .target_w = 2, /* yuv420/422 : 2, others : 1 */ | ||
932 | .target_h = 2, /* yuv420 : 2, others : 1 */ | ||
933 | }; | ||
934 | |||
935 | static struct gsc_variant gsc_v_100_variant = { | ||
936 | .pix_max = &gsc_v_100_max, | ||
937 | .pix_min = &gsc_v_100_min, | ||
938 | .pix_align = &gsc_v_100_align, | ||
939 | .in_buf_cnt = 8, | ||
940 | .out_buf_cnt = 16, | ||
941 | .sc_up_max = 8, | ||
942 | .sc_down_max = 16, | ||
943 | .poly_sc_down_max = 4, | ||
944 | .pre_sc_down_max = 4, | ||
945 | .local_sc_down = 2, | ||
946 | }; | ||
947 | |||
948 | static struct gsc_driverdata gsc_v_100_drvdata = { | ||
949 | .variant = { | ||
950 | [0] = &gsc_v_100_variant, | ||
951 | [1] = &gsc_v_100_variant, | ||
952 | [2] = &gsc_v_100_variant, | ||
953 | [3] = &gsc_v_100_variant, | ||
954 | }, | ||
955 | .num_entities = 4, | ||
956 | .lclk_frequency = 266000000UL, | ||
957 | }; | ||
958 | |||
959 | static struct platform_device_id gsc_driver_ids[] = { | ||
960 | { | ||
961 | .name = "exynos-gsc", | ||
962 | .driver_data = (unsigned long)&gsc_v_100_drvdata, | ||
963 | }, | ||
964 | {}, | ||
965 | }; | ||
966 | MODULE_DEVICE_TABLE(platform, gsc_driver_ids); | ||
967 | |||
968 | static const struct of_device_id exynos_gsc_match[] = { | ||
969 | { .compatible = "samsung,exynos5250-gsc", | ||
970 | .data = &gsc_v_100_drvdata, }, | ||
971 | {}, | ||
972 | }; | ||
973 | MODULE_DEVICE_TABLE(of, exynos_gsc_match); | ||
974 | |||
975 | static void *gsc_get_drv_data(struct platform_device *pdev) | ||
976 | { | ||
977 | struct gsc_driverdata *driver_data = NULL; | ||
978 | |||
979 | if (pdev->dev.of_node) { | ||
980 | const struct of_device_id *match; | ||
981 | match = of_match_node(of_match_ptr(exynos_gsc_match), | ||
982 | pdev->dev.of_node); | ||
983 | if (match) | ||
984 | driver_data = match->data; | ||
985 | } else { | ||
986 | driver_data = (struct gsc_driverdata *) | ||
987 | platform_get_device_id(pdev)->driver_data; | ||
988 | } | ||
989 | |||
990 | return driver_data; | ||
991 | } | ||
992 | |||
993 | static void gsc_clk_put(struct gsc_dev *gsc) | ||
994 | { | ||
995 | if (IS_ERR_OR_NULL(gsc->clock)) | ||
996 | return; | ||
997 | |||
998 | clk_unprepare(gsc->clock); | ||
999 | clk_put(gsc->clock); | ||
1000 | gsc->clock = NULL; | ||
1001 | } | ||
1002 | |||
1003 | static int gsc_clk_get(struct gsc_dev *gsc) | ||
1004 | { | ||
1005 | int ret; | ||
1006 | |||
1007 | dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); | ||
1008 | |||
1009 | gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); | ||
1010 | if (IS_ERR(gsc->clock)) | ||
1011 | goto err_print; | ||
1012 | |||
1013 | ret = clk_prepare(gsc->clock); | ||
1014 | if (ret < 0) { | ||
1015 | clk_put(gsc->clock); | ||
1016 | gsc->clock = NULL; | ||
1017 | goto err; | ||
1018 | } | ||
1019 | |||
1020 | return 0; | ||
1021 | |||
1022 | err: | ||
1023 | dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", | ||
1024 | GSC_CLOCK_GATE_NAME); | ||
1025 | gsc_clk_put(gsc); | ||
1026 | err_print: | ||
1027 | dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", | ||
1028 | GSC_CLOCK_GATE_NAME); | ||
1029 | return -ENXIO; | ||
1030 | } | ||
1031 | |||
1032 | static int gsc_m2m_suspend(struct gsc_dev *gsc) | ||
1033 | { | ||
1034 | unsigned long flags; | ||
1035 | int timeout; | ||
1036 | |||
1037 | spin_lock_irqsave(&gsc->slock, flags); | ||
1038 | if (!gsc_m2m_pending(gsc)) { | ||
1039 | spin_unlock_irqrestore(&gsc->slock, flags); | ||
1040 | return 0; | ||
1041 | } | ||
1042 | clear_bit(ST_M2M_SUSPENDED, &gsc->state); | ||
1043 | set_bit(ST_M2M_SUSPENDING, &gsc->state); | ||
1044 | spin_unlock_irqrestore(&gsc->slock, flags); | ||
1045 | |||
1046 | timeout = wait_event_timeout(gsc->irq_queue, | ||
1047 | test_bit(ST_M2M_SUSPENDED, &gsc->state), | ||
1048 | GSC_SHUTDOWN_TIMEOUT); | ||
1049 | |||
1050 | clear_bit(ST_M2M_SUSPENDING, &gsc->state); | ||
1051 | return timeout == 0 ? -EAGAIN : 0; | ||
1052 | } | ||
1053 | |||
1054 | static int gsc_m2m_resume(struct gsc_dev *gsc) | ||
1055 | { | ||
1056 | unsigned long flags; | ||
1057 | |||
1058 | spin_lock_irqsave(&gsc->slock, flags); | ||
1059 | /* Clear for full H/W setup in first run after resume */ | ||
1060 | gsc->m2m.ctx = NULL; | ||
1061 | spin_unlock_irqrestore(&gsc->slock, flags); | ||
1062 | |||
1063 | if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) | ||
1064 | gsc_m2m_job_finish(gsc->m2m.ctx, | ||
1065 | VB2_BUF_STATE_ERROR); | ||
1066 | return 0; | ||
1067 | } | ||
1068 | |||
1069 | static int gsc_probe(struct platform_device *pdev) | ||
1070 | { | ||
1071 | struct gsc_dev *gsc; | ||
1072 | struct resource *res; | ||
1073 | struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev); | ||
1074 | struct device *dev = &pdev->dev; | ||
1075 | int ret = 0; | ||
1076 | |||
1077 | gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL); | ||
1078 | if (!gsc) | ||
1079 | return -ENOMEM; | ||
1080 | |||
1081 | if (dev->of_node) | ||
1082 | gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); | ||
1083 | else | ||
1084 | gsc->id = pdev->id; | ||
1085 | |||
1086 | if (gsc->id < 0 || gsc->id >= drv_data->num_entities) { | ||
1087 | dev_err(dev, "Invalid platform device id: %d\n", gsc->id); | ||
1088 | return -EINVAL; | ||
1089 | } | ||
1090 | |||
1091 | gsc->variant = drv_data->variant[gsc->id]; | ||
1092 | gsc->pdev = pdev; | ||
1093 | gsc->pdata = dev->platform_data; | ||
1094 | |||
1095 | init_waitqueue_head(&gsc->irq_queue); | ||
1096 | spin_lock_init(&gsc->slock); | ||
1097 | mutex_init(&gsc->lock); | ||
1098 | |||
1099 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1100 | gsc->regs = devm_request_and_ioremap(dev, res); | ||
1101 | if (!gsc->regs) { | ||
1102 | dev_err(dev, "failed to map registers\n"); | ||
1103 | return -ENOENT; | ||
1104 | } | ||
1105 | |||
1106 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
1107 | if (!res) { | ||
1108 | dev_err(dev, "failed to get IRQ resource\n"); | ||
1109 | return -ENXIO; | ||
1110 | } | ||
1111 | |||
1112 | ret = gsc_clk_get(gsc); | ||
1113 | if (ret) | ||
1114 | return ret; | ||
1115 | |||
1116 | ret = devm_request_irq(dev, res->start, gsc_irq_handler, | ||
1117 | 0, pdev->name, gsc); | ||
1118 | if (ret) { | ||
1119 | dev_err(dev, "failed to install irq (%d)\n", ret); | ||
1120 | goto err_clk; | ||
1121 | } | ||
1122 | |||
1123 | ret = gsc_register_m2m_device(gsc); | ||
1124 | if (ret) | ||
1125 | goto err_clk; | ||
1126 | |||
1127 | platform_set_drvdata(pdev, gsc); | ||
1128 | pm_runtime_enable(dev); | ||
1129 | ret = pm_runtime_get_sync(&pdev->dev); | ||
1130 | if (ret < 0) | ||
1131 | goto err_m2m; | ||
1132 | |||
1133 | /* Initialize continious memory allocator */ | ||
1134 | gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); | ||
1135 | if (IS_ERR(gsc->alloc_ctx)) { | ||
1136 | ret = PTR_ERR(gsc->alloc_ctx); | ||
1137 | goto err_pm; | ||
1138 | } | ||
1139 | |||
1140 | dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); | ||
1141 | |||
1142 | pm_runtime_put(dev); | ||
1143 | return 0; | ||
1144 | err_pm: | ||
1145 | pm_runtime_put(dev); | ||
1146 | err_m2m: | ||
1147 | gsc_unregister_m2m_device(gsc); | ||
1148 | err_clk: | ||
1149 | gsc_clk_put(gsc); | ||
1150 | return ret; | ||
1151 | } | ||
1152 | |||
1153 | static int __devexit gsc_remove(struct platform_device *pdev) | ||
1154 | { | ||
1155 | struct gsc_dev *gsc = platform_get_drvdata(pdev); | ||
1156 | |||
1157 | gsc_unregister_m2m_device(gsc); | ||
1158 | |||
1159 | vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); | ||
1160 | pm_runtime_disable(&pdev->dev); | ||
1161 | |||
1162 | dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); | ||
1163 | return 0; | ||
1164 | } | ||
1165 | |||
1166 | static int gsc_runtime_resume(struct device *dev) | ||
1167 | { | ||
1168 | struct gsc_dev *gsc = dev_get_drvdata(dev); | ||
1169 | int ret = 0; | ||
1170 | |||
1171 | pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); | ||
1172 | |||
1173 | ret = clk_enable(gsc->clock); | ||
1174 | if (ret) | ||
1175 | return ret; | ||
1176 | |||
1177 | gsc_hw_set_sw_reset(gsc); | ||
1178 | gsc_wait_reset(gsc); | ||
1179 | |||
1180 | return gsc_m2m_resume(gsc); | ||
1181 | } | ||
1182 | |||
1183 | static int gsc_runtime_suspend(struct device *dev) | ||
1184 | { | ||
1185 | struct gsc_dev *gsc = dev_get_drvdata(dev); | ||
1186 | int ret = 0; | ||
1187 | |||
1188 | ret = gsc_m2m_suspend(gsc); | ||
1189 | if (!ret) | ||
1190 | clk_disable(gsc->clock); | ||
1191 | |||
1192 | pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); | ||
1193 | return ret; | ||
1194 | } | ||
1195 | |||
1196 | static int gsc_resume(struct device *dev) | ||
1197 | { | ||
1198 | struct gsc_dev *gsc = dev_get_drvdata(dev); | ||
1199 | unsigned long flags; | ||
1200 | |||
1201 | pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); | ||
1202 | |||
1203 | /* Do not resume if the device was idle before system suspend */ | ||
1204 | spin_lock_irqsave(&gsc->slock, flags); | ||
1205 | if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) || | ||
1206 | !gsc_m2m_active(gsc)) { | ||
1207 | spin_unlock_irqrestore(&gsc->slock, flags); | ||
1208 | return 0; | ||
1209 | } | ||
1210 | gsc_hw_set_sw_reset(gsc); | ||
1211 | gsc_wait_reset(gsc); | ||
1212 | |||
1213 | spin_unlock_irqrestore(&gsc->slock, flags); | ||
1214 | |||
1215 | return gsc_m2m_resume(gsc); | ||
1216 | } | ||
1217 | |||
1218 | static int gsc_suspend(struct device *dev) | ||
1219 | { | ||
1220 | struct gsc_dev *gsc = dev_get_drvdata(dev); | ||
1221 | |||
1222 | pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); | ||
1223 | |||
1224 | if (test_and_set_bit(ST_SUSPEND, &gsc->state)) | ||
1225 | return 0; | ||
1226 | |||
1227 | return gsc_m2m_suspend(gsc); | ||
1228 | } | ||
1229 | |||
1230 | static const struct dev_pm_ops gsc_pm_ops = { | ||
1231 | .suspend = gsc_suspend, | ||
1232 | .resume = gsc_resume, | ||
1233 | .runtime_suspend = gsc_runtime_suspend, | ||
1234 | .runtime_resume = gsc_runtime_resume, | ||
1235 | }; | ||
1236 | |||
1237 | static struct platform_driver gsc_driver = { | ||
1238 | .probe = gsc_probe, | ||
1239 | .remove = __devexit_p(gsc_remove), | ||
1240 | .id_table = gsc_driver_ids, | ||
1241 | .driver = { | ||
1242 | .name = GSC_MODULE_NAME, | ||
1243 | .owner = THIS_MODULE, | ||
1244 | .pm = &gsc_pm_ops, | ||
1245 | .of_match_table = exynos_gsc_match, | ||
1246 | } | ||
1247 | }; | ||
1248 | |||
1249 | module_platform_driver(gsc_driver); | ||
1250 | |||
1251 | MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@samsung.com>"); | ||
1252 | MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver"); | ||
1253 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h new file mode 100644 index 000000000000..5f157efd24f0 --- /dev/null +++ b/drivers/media/platform/exynos-gsc/gsc-core.h | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com | ||
4 | * | ||
5 | * header file for Samsung EXYNOS5 SoC series G-Scaler driver | ||
6 | |||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef GSC_CORE_H_ | ||
13 | #define GSC_CORE_H_ | ||
14 | |||
15 | #include <linux/delay.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/videodev2.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/pm_runtime.h> | ||
22 | #include <media/videobuf2-core.h> | ||
23 | #include <media/v4l2-ctrls.h> | ||
24 | #include <media/v4l2-device.h> | ||
25 | #include <media/v4l2-mem2mem.h> | ||
26 | #include <media/v4l2-mediabus.h> | ||
27 | #include <media/videobuf2-dma-contig.h> | ||
28 | |||
29 | #include "gsc-regs.h" | ||
30 | |||
31 | #define CONFIG_VB2_GSC_DMA_CONTIG 1 | ||
32 | #define GSC_MODULE_NAME "exynos-gsc" | ||
33 | |||
34 | #define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) | ||
35 | #define GSC_MAX_DEVS 4 | ||
36 | #define GSC_M2M_BUF_NUM 0 | ||
37 | #define GSC_MAX_CTRL_NUM 10 | ||
38 | #define GSC_SC_ALIGN_4 4 | ||
39 | #define GSC_SC_ALIGN_2 2 | ||
40 | #define DEFAULT_CSC_EQ 1 | ||
41 | #define DEFAULT_CSC_RANGE 1 | ||
42 | |||
43 | #define GSC_PARAMS (1 << 0) | ||
44 | #define GSC_SRC_FMT (1 << 1) | ||
45 | #define GSC_DST_FMT (1 << 2) | ||
46 | #define GSC_CTX_M2M (1 << 3) | ||
47 | #define GSC_CTX_STOP_REQ (1 << 6) | ||
48 | |||
49 | enum gsc_dev_flags { | ||
50 | /* for global */ | ||
51 | ST_SUSPEND, | ||
52 | |||
53 | /* for m2m node */ | ||
54 | ST_M2M_OPEN, | ||
55 | ST_M2M_RUN, | ||
56 | ST_M2M_PEND, | ||
57 | ST_M2M_SUSPENDED, | ||
58 | ST_M2M_SUSPENDING, | ||
59 | }; | ||
60 | |||
61 | enum gsc_irq { | ||
62 | GSC_IRQ_DONE, | ||
63 | GSC_IRQ_OVERRUN | ||
64 | }; | ||
65 | |||
66 | /** | ||
67 | * enum gsc_datapath - the path of data used for G-Scaler | ||
68 | * @GSC_CAMERA: from camera | ||
69 | * @GSC_DMA: from/to DMA | ||
70 | * @GSC_LOCAL: to local path | ||
71 | * @GSC_WRITEBACK: from FIMD | ||
72 | */ | ||
73 | enum gsc_datapath { | ||
74 | GSC_CAMERA = 0x1, | ||
75 | GSC_DMA, | ||
76 | GSC_MIXER, | ||
77 | GSC_FIMD, | ||
78 | GSC_WRITEBACK, | ||
79 | }; | ||
80 | |||
81 | enum gsc_color_fmt { | ||
82 | GSC_RGB = 0x1, | ||
83 | GSC_YUV420 = 0x2, | ||
84 | GSC_YUV422 = 0x4, | ||
85 | GSC_YUV444 = 0x8, | ||
86 | }; | ||
87 | |||
88 | enum gsc_yuv_fmt { | ||
89 | GSC_LSB_Y = 0x10, | ||
90 | GSC_LSB_C, | ||
91 | GSC_CBCR = 0x20, | ||
92 | GSC_CRCB, | ||
93 | }; | ||
94 | |||
95 | #define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh) | ||
96 | #define is_rgb(x) (!!((x) & 0x1)) | ||
97 | #define is_yuv420(x) (!!((x) & 0x2)) | ||
98 | #define is_yuv422(x) (!!((x) & 0x4)) | ||
99 | |||
100 | #define gsc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) | ||
101 | #define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) | ||
102 | #define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state) | ||
103 | |||
104 | #define ctrl_to_ctx(__ctrl) \ | ||
105 | container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler) | ||
106 | /** | ||
107 | * struct gsc_fmt - the driver's internal color format data | ||
108 | * @mbus_code: Media Bus pixel code, -1 if not applicable | ||
109 | * @name: format description | ||
110 | * @pixelformat: the fourcc code for this format, 0 if not applicable | ||
111 | * @yorder: Y/C order | ||
112 | * @corder: Chrominance order control | ||
113 | * @num_planes: number of physically non-contiguous data planes | ||
114 | * @nr_comp: number of physically contiguous data planes | ||
115 | * @depth: per plane driver's private 'number of bits per pixel' | ||
116 | * @flags: flags indicating which operation mode format applies to | ||
117 | */ | ||
118 | struct gsc_fmt { | ||
119 | enum v4l2_mbus_pixelcode mbus_code; | ||
120 | char *name; | ||
121 | u32 pixelformat; | ||
122 | u32 color; | ||
123 | u32 yorder; | ||
124 | u32 corder; | ||
125 | u16 num_planes; | ||
126 | u16 num_comp; | ||
127 | u8 depth[VIDEO_MAX_PLANES]; | ||
128 | u32 flags; | ||
129 | }; | ||
130 | |||
131 | /** | ||
132 | * struct gsc_input_buf - the driver's video buffer | ||
133 | * @vb: videobuf2 buffer | ||
134 | * @list : linked list structure for buffer queue | ||
135 | * @idx : index of G-Scaler input buffer | ||
136 | */ | ||
137 | struct gsc_input_buf { | ||
138 | struct vb2_buffer vb; | ||
139 | struct list_head list; | ||
140 | int idx; | ||
141 | }; | ||
142 | |||
143 | /** | ||
144 | * struct gsc_addr - the G-Scaler physical address set | ||
145 | * @y: luminance plane address | ||
146 | * @cb: Cb plane address | ||
147 | * @cr: Cr plane address | ||
148 | */ | ||
149 | struct gsc_addr { | ||
150 | dma_addr_t y; | ||
151 | dma_addr_t cb; | ||
152 | dma_addr_t cr; | ||
153 | }; | ||
154 | |||
155 | /* struct gsc_ctrls - the G-Scaler control set | ||
156 | * @rotate: rotation degree | ||
157 | * @hflip: horizontal flip | ||
158 | * @vflip: vertical flip | ||
159 | * @global_alpha: the alpha value of current frame | ||
160 | */ | ||
161 | struct gsc_ctrls { | ||
162 | struct v4l2_ctrl *rotate; | ||
163 | struct v4l2_ctrl *hflip; | ||
164 | struct v4l2_ctrl *vflip; | ||
165 | struct v4l2_ctrl *global_alpha; | ||
166 | }; | ||
167 | |||
168 | /** | ||
169 | * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler | ||
170 | * @pre_shfactor: pre sclaer shift factor | ||
171 | * @pre_hratio: horizontal ratio of the prescaler | ||
172 | * @pre_vratio: vertical ratio of the prescaler | ||
173 | * @main_hratio: the main scaler's horizontal ratio | ||
174 | * @main_vratio: the main scaler's vertical ratio | ||
175 | */ | ||
176 | struct gsc_scaler { | ||
177 | u32 pre_shfactor; | ||
178 | u32 pre_hratio; | ||
179 | u32 pre_vratio; | ||
180 | u32 main_hratio; | ||
181 | u32 main_vratio; | ||
182 | }; | ||
183 | |||
184 | struct gsc_dev; | ||
185 | |||
186 | struct gsc_ctx; | ||
187 | |||
188 | /** | ||
189 | * struct gsc_frame - source/target frame properties | ||
190 | * @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH | ||
191 | * @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT | ||
192 | * @crop: cropped(source)/scaled(destination) size | ||
193 | * @payload: image size in bytes (w x h x bpp) | ||
194 | * @addr: image frame buffer physical addresses | ||
195 | * @fmt: G-Scaler color format pointer | ||
196 | * @colorspace: value indicating v4l2_colorspace | ||
197 | * @alpha: frame's alpha value | ||
198 | */ | ||
199 | struct gsc_frame { | ||
200 | u32 f_width; | ||
201 | u32 f_height; | ||
202 | struct v4l2_rect crop; | ||
203 | unsigned long payload[VIDEO_MAX_PLANES]; | ||
204 | struct gsc_addr addr; | ||
205 | const struct gsc_fmt *fmt; | ||
206 | u32 colorspace; | ||
207 | u8 alpha; | ||
208 | }; | ||
209 | |||
210 | /** | ||
211 | * struct gsc_m2m_device - v4l2 memory-to-memory device data | ||
212 | * @vfd: the video device node for v4l2 m2m mode | ||
213 | * @m2m_dev: v4l2 memory-to-memory device data | ||
214 | * @ctx: hardware context data | ||
215 | * @refcnt: the reference counter | ||
216 | */ | ||
217 | struct gsc_m2m_device { | ||
218 | struct video_device *vfd; | ||
219 | struct v4l2_m2m_dev *m2m_dev; | ||
220 | struct gsc_ctx *ctx; | ||
221 | int refcnt; | ||
222 | }; | ||
223 | |||
224 | /** | ||
225 | * struct gsc_pix_max - image pixel size limits in various IP configurations | ||
226 | * | ||
227 | * @org_scaler_bypass_w: max pixel width when the scaler is disabled | ||
228 | * @org_scaler_bypass_h: max pixel height when the scaler is disabled | ||
229 | * @org_scaler_input_w: max pixel width when the scaler is enabled | ||
230 | * @org_scaler_input_h: max pixel height when the scaler is enabled | ||
231 | * @real_rot_dis_w: max pixel src cropped height with the rotator is off | ||
232 | * @real_rot_dis_h: max pixel src croppped width with the rotator is off | ||
233 | * @real_rot_en_w: max pixel src cropped width with the rotator is on | ||
234 | * @real_rot_en_h: max pixel src cropped height with the rotator is on | ||
235 | * @target_rot_dis_w: max pixel dst scaled width with the rotator is off | ||
236 | * @target_rot_dis_h: max pixel dst scaled height with the rotator is off | ||
237 | * @target_rot_en_w: max pixel dst scaled width with the rotator is on | ||
238 | * @target_rot_en_h: max pixel dst scaled height with the rotator is on | ||
239 | */ | ||
240 | struct gsc_pix_max { | ||
241 | u16 org_scaler_bypass_w; | ||
242 | u16 org_scaler_bypass_h; | ||
243 | u16 org_scaler_input_w; | ||
244 | u16 org_scaler_input_h; | ||
245 | u16 real_rot_dis_w; | ||
246 | u16 real_rot_dis_h; | ||
247 | u16 real_rot_en_w; | ||
248 | u16 real_rot_en_h; | ||
249 | u16 target_rot_dis_w; | ||
250 | u16 target_rot_dis_h; | ||
251 | u16 target_rot_en_w; | ||
252 | u16 target_rot_en_h; | ||
253 | }; | ||
254 | |||
255 | /** | ||
256 | * struct gsc_pix_min - image pixel size limits in various IP configurations | ||
257 | * | ||
258 | * @org_w: minimum source pixel width | ||
259 | * @org_h: minimum source pixel height | ||
260 | * @real_w: minimum input crop pixel width | ||
261 | * @real_h: minimum input crop pixel height | ||
262 | * @target_rot_dis_w: minimum output scaled pixel height when rotator is off | ||
263 | * @target_rot_dis_h: minimum output scaled pixel height when rotator is off | ||
264 | * @target_rot_en_w: minimum output scaled pixel height when rotator is on | ||
265 | * @target_rot_en_h: minimum output scaled pixel height when rotator is on | ||
266 | */ | ||
267 | struct gsc_pix_min { | ||
268 | u16 org_w; | ||
269 | u16 org_h; | ||
270 | u16 real_w; | ||
271 | u16 real_h; | ||
272 | u16 target_rot_dis_w; | ||
273 | u16 target_rot_dis_h; | ||
274 | u16 target_rot_en_w; | ||
275 | u16 target_rot_en_h; | ||
276 | }; | ||
277 | |||
278 | struct gsc_pix_align { | ||
279 | u16 org_h; | ||
280 | u16 org_w; | ||
281 | u16 offset_h; | ||
282 | u16 real_w; | ||
283 | u16 real_h; | ||
284 | u16 target_w; | ||
285 | u16 target_h; | ||
286 | }; | ||
287 | |||
288 | /** | ||
289 | * struct gsc_variant - G-Scaler variant information | ||
290 | */ | ||
291 | struct gsc_variant { | ||
292 | struct gsc_pix_max *pix_max; | ||
293 | struct gsc_pix_min *pix_min; | ||
294 | struct gsc_pix_align *pix_align; | ||
295 | u16 in_buf_cnt; | ||
296 | u16 out_buf_cnt; | ||
297 | u16 sc_up_max; | ||
298 | u16 sc_down_max; | ||
299 | u16 poly_sc_down_max; | ||
300 | u16 pre_sc_down_max; | ||
301 | u16 local_sc_down; | ||
302 | }; | ||
303 | |||
304 | /** | ||
305 | * struct gsc_driverdata - per device type driver data for init time. | ||
306 | * | ||
307 | * @variant: the variant information for this driver. | ||
308 | * @lclk_frequency: G-Scaler clock frequency | ||
309 | * @num_entities: the number of g-scalers | ||
310 | */ | ||
311 | struct gsc_driverdata { | ||
312 | struct gsc_variant *variant[GSC_MAX_DEVS]; | ||
313 | unsigned long lclk_frequency; | ||
314 | int num_entities; | ||
315 | }; | ||
316 | |||
317 | /** | ||
318 | * struct gsc_dev - abstraction for G-Scaler entity | ||
319 | * @slock: the spinlock protecting this data structure | ||
320 | * @lock: the mutex protecting this data structure | ||
321 | * @pdev: pointer to the G-Scaler platform device | ||
322 | * @variant: the IP variant information | ||
323 | * @id: G-Scaler device index (0..GSC_MAX_DEVS) | ||
324 | * @clock: clocks required for G-Scaler operation | ||
325 | * @regs: the mapped hardware registers | ||
326 | * @irq_queue: interrupt handler waitqueue | ||
327 | * @m2m: memory-to-memory V4L2 device information | ||
328 | * @state: flags used to synchronize m2m and capture mode operation | ||
329 | * @alloc_ctx: videobuf2 memory allocator context | ||
330 | * @vdev: video device for G-Scaler instance | ||
331 | */ | ||
332 | struct gsc_dev { | ||
333 | spinlock_t slock; | ||
334 | struct mutex lock; | ||
335 | struct platform_device *pdev; | ||
336 | struct gsc_variant *variant; | ||
337 | u16 id; | ||
338 | struct clk *clock; | ||
339 | void __iomem *regs; | ||
340 | wait_queue_head_t irq_queue; | ||
341 | struct gsc_m2m_device m2m; | ||
342 | struct exynos_platform_gscaler *pdata; | ||
343 | unsigned long state; | ||
344 | struct vb2_alloc_ctx *alloc_ctx; | ||
345 | struct video_device vdev; | ||
346 | }; | ||
347 | |||
348 | /** | ||
349 | * gsc_ctx - the device context data | ||
350 | * @s_frame: source frame properties | ||
351 | * @d_frame: destination frame properties | ||
352 | * @in_path: input mode (DMA or camera) | ||
353 | * @out_path: output mode (DMA or FIFO) | ||
354 | * @scaler: image scaler properties | ||
355 | * @flags: additional flags for image conversion | ||
356 | * @state: flags to keep track of user configuration | ||
357 | * @gsc_dev: the G-Scaler device this context applies to | ||
358 | * @m2m_ctx: memory-to-memory device context | ||
359 | * @fh: v4l2 file handle | ||
360 | * @ctrl_handler: v4l2 controls handler | ||
361 | * @gsc_ctrls G-Scaler control set | ||
362 | * @ctrls_rdy: true if the control handler is initialized | ||
363 | */ | ||
364 | struct gsc_ctx { | ||
365 | struct gsc_frame s_frame; | ||
366 | struct gsc_frame d_frame; | ||
367 | enum gsc_datapath in_path; | ||
368 | enum gsc_datapath out_path; | ||
369 | struct gsc_scaler scaler; | ||
370 | u32 flags; | ||
371 | u32 state; | ||
372 | int rotation; | ||
373 | unsigned int hflip:1; | ||
374 | unsigned int vflip:1; | ||
375 | struct gsc_dev *gsc_dev; | ||
376 | struct v4l2_m2m_ctx *m2m_ctx; | ||
377 | struct v4l2_fh fh; | ||
378 | struct v4l2_ctrl_handler ctrl_handler; | ||
379 | struct gsc_ctrls gsc_ctrls; | ||
380 | bool ctrls_rdy; | ||
381 | }; | ||
382 | |||
383 | void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm); | ||
384 | int gsc_register_m2m_device(struct gsc_dev *gsc); | ||
385 | void gsc_unregister_m2m_device(struct gsc_dev *gsc); | ||
386 | void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); | ||
387 | |||
388 | u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); | ||
389 | const struct gsc_fmt *get_format(int index); | ||
390 | const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); | ||
391 | int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f); | ||
392 | int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); | ||
393 | void gsc_set_frame_size(struct gsc_frame *frame, int width, int height); | ||
394 | int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); | ||
395 | void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h); | ||
396 | int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr); | ||
397 | int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr); | ||
398 | int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, | ||
399 | u32 *ratio); | ||
400 | void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh); | ||
401 | void gsc_check_src_scale_info(struct gsc_variant *var, | ||
402 | struct gsc_frame *s_frame, | ||
403 | u32 *wratio, u32 tx, u32 ty, u32 *hratio); | ||
404 | int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw, | ||
405 | int dh, int rot, int out_path); | ||
406 | int gsc_set_scaler_info(struct gsc_ctx *ctx); | ||
407 | int gsc_ctrls_create(struct gsc_ctx *ctx); | ||
408 | void gsc_ctrls_delete(struct gsc_ctx *ctx); | ||
409 | int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, | ||
410 | struct gsc_frame *frame, struct gsc_addr *addr); | ||
411 | |||
412 | static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx) | ||
413 | { | ||
414 | unsigned long flags; | ||
415 | |||
416 | spin_lock_irqsave(&ctx->gsc_dev->slock, flags); | ||
417 | ctx->state |= state; | ||
418 | spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); | ||
419 | } | ||
420 | |||
421 | static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) | ||
422 | { | ||
423 | unsigned long flags; | ||
424 | |||
425 | spin_lock_irqsave(&ctx->gsc_dev->slock, flags); | ||
426 | ctx->state &= ~state; | ||
427 | spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); | ||
428 | } | ||
429 | |||
430 | static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) | ||
431 | { | ||
432 | u32 cfg = readl(dev->regs + GSC_ENABLE); | ||
433 | |||
434 | if (on) | ||
435 | cfg |= GSC_ENABLE_ON; | ||
436 | else | ||
437 | cfg &= ~GSC_ENABLE_ON; | ||
438 | |||
439 | writel(cfg, dev->regs + GSC_ENABLE); | ||
440 | } | ||
441 | |||
442 | static inline int gsc_hw_get_irq_status(struct gsc_dev *dev) | ||
443 | { | ||
444 | u32 cfg = readl(dev->regs + GSC_IRQ); | ||
445 | if (cfg & GSC_IRQ_STATUS_OR_IRQ) | ||
446 | return GSC_IRQ_OVERRUN; | ||
447 | else | ||
448 | return GSC_IRQ_DONE; | ||
449 | |||
450 | } | ||
451 | |||
452 | static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) | ||
453 | { | ||
454 | u32 cfg = readl(dev->regs + GSC_IRQ); | ||
455 | if (irq == GSC_IRQ_OVERRUN) | ||
456 | cfg |= GSC_IRQ_STATUS_OR_IRQ; | ||
457 | else if (irq == GSC_IRQ_DONE) | ||
458 | cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ; | ||
459 | writel(cfg, dev->regs + GSC_IRQ); | ||
460 | } | ||
461 | |||
462 | static inline void gsc_lock(struct vb2_queue *vq) | ||
463 | { | ||
464 | struct gsc_ctx *ctx = vb2_get_drv_priv(vq); | ||
465 | mutex_lock(&ctx->gsc_dev->lock); | ||
466 | } | ||
467 | |||
468 | static inline void gsc_unlock(struct vb2_queue *vq) | ||
469 | { | ||
470 | struct gsc_ctx *ctx = vb2_get_drv_priv(vq); | ||
471 | mutex_unlock(&ctx->gsc_dev->lock); | ||
472 | } | ||
473 | |||
474 | static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) | ||
475 | { | ||
476 | unsigned long flags; | ||
477 | bool ret; | ||
478 | |||
479 | spin_lock_irqsave(&ctx->gsc_dev->slock, flags); | ||
480 | ret = (ctx->state & mask) == mask; | ||
481 | spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); | ||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx, | ||
486 | enum v4l2_buf_type type) | ||
487 | { | ||
488 | struct gsc_frame *frame; | ||
489 | |||
490 | if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) { | ||
491 | frame = &ctx->s_frame; | ||
492 | } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) { | ||
493 | frame = &ctx->d_frame; | ||
494 | } else { | ||
495 | pr_err("Wrong buffer/video queue type (%d)", type); | ||
496 | return ERR_PTR(-EINVAL); | ||
497 | } | ||
498 | |||
499 | return frame; | ||
500 | } | ||
501 | |||
502 | void gsc_hw_set_sw_reset(struct gsc_dev *dev); | ||
503 | int gsc_wait_reset(struct gsc_dev *dev); | ||
504 | |||
505 | void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask); | ||
506 | void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask); | ||
507 | void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable); | ||
508 | void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable); | ||
509 | void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, | ||
510 | int index); | ||
511 | void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr, | ||
512 | int index); | ||
513 | void gsc_hw_set_input_path(struct gsc_ctx *ctx); | ||
514 | void gsc_hw_set_in_size(struct gsc_ctx *ctx); | ||
515 | void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx); | ||
516 | void gsc_hw_set_in_image_format(struct gsc_ctx *ctx); | ||
517 | void gsc_hw_set_output_path(struct gsc_ctx *ctx); | ||
518 | void gsc_hw_set_out_size(struct gsc_ctx *ctx); | ||
519 | void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx); | ||
520 | void gsc_hw_set_out_image_format(struct gsc_ctx *ctx); | ||
521 | void gsc_hw_set_prescaler(struct gsc_ctx *ctx); | ||
522 | void gsc_hw_set_mainscaler(struct gsc_ctx *ctx); | ||
523 | void gsc_hw_set_rotation(struct gsc_ctx *ctx); | ||
524 | void gsc_hw_set_global_alpha(struct gsc_ctx *ctx); | ||
525 | void gsc_hw_set_sfr_update(struct gsc_ctx *ctx); | ||
526 | |||
527 | #endif /* GSC_CORE_H_ */ | ||