perf tools: Don't clone maps from parent when synthesizing forks
[linux/fpc-iii.git] / drivers / media / platform / exynos-gsc / gsc-core.c
blob17854a379243ce97fa65776320147b7e30749d6a
1 /*
2 * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
5 * Samsung EXYNOS5 SoC series G-Scaler driver
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.
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/types.h>
16 #include <linux/errno.h>
17 #include <linux/bug.h>
18 #include <linux/interrupt.h>
19 #include <linux/workqueue.h>
20 #include <linux/device.h>
21 #include <linux/platform_device.h>
22 #include <linux/list.h>
23 #include <linux/io.h>
24 #include <linux/slab.h>
25 #include <linux/clk.h>
26 #include <linux/of.h>
27 #include <linux/of_device.h>
28 #include <media/v4l2-ioctl.h>
30 #include "gsc-core.h"
32 static const struct gsc_fmt gsc_formats[] = {
34 .name = "RGB565",
35 .pixelformat = V4L2_PIX_FMT_RGB565X,
36 .depth = { 16 },
37 .color = GSC_RGB,
38 .num_planes = 1,
39 .num_comp = 1,
40 }, {
41 .name = "BGRX-8-8-8-8, 32 bpp",
42 .pixelformat = V4L2_PIX_FMT_BGR32,
43 .depth = { 32 },
44 .color = GSC_RGB,
45 .num_planes = 1,
46 .num_comp = 1,
47 }, {
48 .name = "YUV 4:2:2 packed, YCbYCr",
49 .pixelformat = V4L2_PIX_FMT_YUYV,
50 .depth = { 16 },
51 .color = GSC_YUV422,
52 .yorder = GSC_LSB_Y,
53 .corder = GSC_CBCR,
54 .num_planes = 1,
55 .num_comp = 1,
56 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
57 }, {
58 .name = "YUV 4:2:2 packed, CbYCrY",
59 .pixelformat = V4L2_PIX_FMT_UYVY,
60 .depth = { 16 },
61 .color = GSC_YUV422,
62 .yorder = GSC_LSB_C,
63 .corder = GSC_CBCR,
64 .num_planes = 1,
65 .num_comp = 1,
66 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
67 }, {
68 .name = "YUV 4:2:2 packed, CrYCbY",
69 .pixelformat = V4L2_PIX_FMT_VYUY,
70 .depth = { 16 },
71 .color = GSC_YUV422,
72 .yorder = GSC_LSB_C,
73 .corder = GSC_CRCB,
74 .num_planes = 1,
75 .num_comp = 1,
76 .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
77 }, {
78 .name = "YUV 4:2:2 packed, YCrYCb",
79 .pixelformat = V4L2_PIX_FMT_YVYU,
80 .depth = { 16 },
81 .color = GSC_YUV422,
82 .yorder = GSC_LSB_Y,
83 .corder = GSC_CRCB,
84 .num_planes = 1,
85 .num_comp = 1,
86 .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
87 }, {
88 .name = "YUV 4:4:4 planar, YCbYCr",
89 .pixelformat = V4L2_PIX_FMT_YUV32,
90 .depth = { 32 },
91 .color = GSC_YUV444,
92 .yorder = GSC_LSB_Y,
93 .corder = GSC_CBCR,
94 .num_planes = 1,
95 .num_comp = 1,
96 }, {
97 .name = "YUV 4:2:2 planar, Y/Cb/Cr",
98 .pixelformat = V4L2_PIX_FMT_YUV422P,
99 .depth = { 16 },
100 .color = GSC_YUV422,
101 .yorder = GSC_LSB_Y,
102 .corder = GSC_CBCR,
103 .num_planes = 1,
104 .num_comp = 3,
105 }, {
106 .name = "YUV 4:2:2 planar, Y/CbCr",
107 .pixelformat = V4L2_PIX_FMT_NV16,
108 .depth = { 16 },
109 .color = GSC_YUV422,
110 .yorder = GSC_LSB_Y,
111 .corder = GSC_CBCR,
112 .num_planes = 1,
113 .num_comp = 2,
114 }, {
115 .name = "YUV 4:2:2 non-contig, Y/CbCr",
116 .pixelformat = V4L2_PIX_FMT_NV16M,
117 .depth = { 8, 8 },
118 .color = GSC_YUV422,
119 .yorder = GSC_LSB_Y,
120 .corder = GSC_CBCR,
121 .num_planes = 2,
122 .num_comp = 2,
123 }, {
124 .name = "YUV 4:2:2 planar, Y/CrCb",
125 .pixelformat = V4L2_PIX_FMT_NV61,
126 .depth = { 16 },
127 .color = GSC_YUV422,
128 .yorder = GSC_LSB_Y,
129 .corder = GSC_CRCB,
130 .num_planes = 1,
131 .num_comp = 2,
132 }, {
133 .name = "YUV 4:2:2 non-contig, Y/CrCb",
134 .pixelformat = V4L2_PIX_FMT_NV61M,
135 .depth = { 8, 8 },
136 .color = GSC_YUV422,
137 .yorder = GSC_LSB_Y,
138 .corder = GSC_CRCB,
139 .num_planes = 2,
140 .num_comp = 2,
141 }, {
142 .name = "YUV 4:2:0 planar, YCbCr",
143 .pixelformat = V4L2_PIX_FMT_YUV420,
144 .depth = { 12 },
145 .color = GSC_YUV420,
146 .yorder = GSC_LSB_Y,
147 .corder = GSC_CBCR,
148 .num_planes = 1,
149 .num_comp = 3,
150 }, {
151 .name = "YUV 4:2:0 planar, YCrCb",
152 .pixelformat = V4L2_PIX_FMT_YVU420,
153 .depth = { 12 },
154 .color = GSC_YUV420,
155 .yorder = GSC_LSB_Y,
156 .corder = GSC_CRCB,
157 .num_planes = 1,
158 .num_comp = 3,
160 }, {
161 .name = "YUV 4:2:0 planar, Y/CbCr",
162 .pixelformat = V4L2_PIX_FMT_NV12,
163 .depth = { 12 },
164 .color = GSC_YUV420,
165 .yorder = GSC_LSB_Y,
166 .corder = GSC_CBCR,
167 .num_planes = 1,
168 .num_comp = 2,
169 }, {
170 .name = "YUV 4:2:0 planar, Y/CrCb",
171 .pixelformat = V4L2_PIX_FMT_NV21,
172 .depth = { 12 },
173 .color = GSC_YUV420,
174 .yorder = GSC_LSB_Y,
175 .corder = GSC_CRCB,
176 .num_planes = 1,
177 .num_comp = 2,
178 }, {
179 .name = "YUV 4:2:0 non-contig. 2p, Y/CrCb",
180 .pixelformat = V4L2_PIX_FMT_NV21M,
181 .depth = { 8, 4 },
182 .color = GSC_YUV420,
183 .yorder = GSC_LSB_Y,
184 .corder = GSC_CRCB,
185 .num_planes = 2,
186 .num_comp = 2,
187 }, {
188 .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr",
189 .pixelformat = V4L2_PIX_FMT_NV12M,
190 .depth = { 8, 4 },
191 .color = GSC_YUV420,
192 .yorder = GSC_LSB_Y,
193 .corder = GSC_CBCR,
194 .num_planes = 2,
195 .num_comp = 2,
196 }, {
197 .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr",
198 .pixelformat = V4L2_PIX_FMT_YUV420M,
199 .depth = { 8, 2, 2 },
200 .color = GSC_YUV420,
201 .yorder = GSC_LSB_Y,
202 .corder = GSC_CBCR,
203 .num_planes = 3,
204 .num_comp = 3,
205 }, {
206 .name = "YUV 4:2:0 non-contig. 3p, Y/Cr/Cb",
207 .pixelformat = V4L2_PIX_FMT_YVU420M,
208 .depth = { 8, 2, 2 },
209 .color = GSC_YUV420,
210 .yorder = GSC_LSB_Y,
211 .corder = GSC_CRCB,
212 .num_planes = 3,
213 .num_comp = 3,
214 }, {
215 .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled",
216 .pixelformat = V4L2_PIX_FMT_NV12MT_16X16,
217 .depth = { 8, 4 },
218 .color = GSC_YUV420,
219 .yorder = GSC_LSB_Y,
220 .corder = GSC_CBCR,
221 .num_planes = 2,
222 .num_comp = 2,
226 const struct gsc_fmt *get_format(int index)
228 if (index >= ARRAY_SIZE(gsc_formats))
229 return NULL;
231 return (struct gsc_fmt *)&gsc_formats[index];
234 const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index)
236 const struct gsc_fmt *fmt, *def_fmt = NULL;
237 unsigned int i;
239 if (index >= ARRAY_SIZE(gsc_formats))
240 return NULL;
242 for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) {
243 fmt = get_format(i);
244 if (pixelformat && fmt->pixelformat == *pixelformat)
245 return fmt;
246 if (mbus_code && fmt->mbus_code == *mbus_code)
247 return fmt;
248 if (index == i)
249 def_fmt = fmt;
251 return def_fmt;
255 void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
257 frame->f_width = width;
258 frame->f_height = height;
259 frame->crop.width = width;
260 frame->crop.height = height;
261 frame->crop.left = 0;
262 frame->crop.top = 0;
265 int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
266 u32 *ratio)
268 if ((dst > src) || (dst >= src / var->poly_sc_down_max)) {
269 *ratio = 1;
270 return 0;
273 if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) {
274 pr_err("Exceeded maximum downscaling ratio (1/16))");
275 return -EINVAL;
278 *ratio = (dst > (src / 8)) ? 2 : 4;
280 return 0;
283 void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
285 if (hratio == 4 && vratio == 4)
286 *sh = 4;
287 else if ((hratio == 4 && vratio == 2) ||
288 (hratio == 2 && vratio == 4))
289 *sh = 3;
290 else if ((hratio == 4 && vratio == 1) ||
291 (hratio == 1 && vratio == 4) ||
292 (hratio == 2 && vratio == 2))
293 *sh = 2;
294 else if (hratio == 1 && vratio == 1)
295 *sh = 0;
296 else
297 *sh = 1;
300 void gsc_check_src_scale_info(struct gsc_variant *var,
301 struct gsc_frame *s_frame, u32 *wratio,
302 u32 tx, u32 ty, u32 *hratio)
304 int remainder = 0, walign, halign;
306 if (is_yuv420(s_frame->fmt->color)) {
307 walign = GSC_SC_ALIGN_4;
308 halign = GSC_SC_ALIGN_4;
309 } else if (is_yuv422(s_frame->fmt->color)) {
310 walign = GSC_SC_ALIGN_4;
311 halign = GSC_SC_ALIGN_2;
312 } else {
313 walign = GSC_SC_ALIGN_2;
314 halign = GSC_SC_ALIGN_2;
317 remainder = s_frame->crop.width % (*wratio * walign);
318 if (remainder) {
319 s_frame->crop.width -= remainder;
320 gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
321 pr_info("cropped src width size is recalculated from %d to %d",
322 s_frame->crop.width + remainder, s_frame->crop.width);
325 remainder = s_frame->crop.height % (*hratio * halign);
326 if (remainder) {
327 s_frame->crop.height -= remainder;
328 gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
329 pr_info("cropped src height size is recalculated from %d to %d",
330 s_frame->crop.height + remainder, s_frame->crop.height);
334 int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
336 const struct gsc_fmt *fmt;
338 fmt = find_fmt(NULL, NULL, f->index);
339 if (!fmt)
340 return -EINVAL;
342 strlcpy(f->description, fmt->name, sizeof(f->description));
343 f->pixelformat = fmt->pixelformat;
345 return 0;
348 static int get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index, u32 *ret_addr)
350 if (frm->addr.y == addr) {
351 *index = 0;
352 *ret_addr = frm->addr.y;
353 } else if (frm->addr.cb == addr) {
354 *index = 1;
355 *ret_addr = frm->addr.cb;
356 } else if (frm->addr.cr == addr) {
357 *index = 2;
358 *ret_addr = frm->addr.cr;
359 } else {
360 pr_err("Plane address is wrong");
361 return -EINVAL;
363 return 0;
366 void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm)
368 u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
369 f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
371 f_chk_addr = frm->addr.y;
372 f_chk_len = frm->payload[0];
373 if (frm->fmt->num_planes == 2) {
374 s_chk_addr = frm->addr.cb;
375 s_chk_len = frm->payload[1];
376 } else if (frm->fmt->num_planes == 3) {
377 u32 low_addr, low_plane, mid_addr, mid_plane;
378 u32 high_addr, high_plane;
379 u32 t_min, t_max;
381 t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr);
382 if (get_plane_info(frm, t_min, &low_plane, &low_addr))
383 return;
384 t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr);
385 if (get_plane_info(frm, t_max, &high_plane, &high_addr))
386 return;
388 mid_plane = 3 - (low_plane + high_plane);
389 if (mid_plane == 0)
390 mid_addr = frm->addr.y;
391 else if (mid_plane == 1)
392 mid_addr = frm->addr.cb;
393 else if (mid_plane == 2)
394 mid_addr = frm->addr.cr;
395 else
396 return;
398 f_chk_addr = low_addr;
399 if (mid_addr + frm->payload[mid_plane] - low_addr >
400 high_addr + frm->payload[high_plane] - mid_addr) {
401 f_chk_len = frm->payload[low_plane];
402 s_chk_addr = mid_addr;
403 s_chk_len = high_addr +
404 frm->payload[high_plane] - mid_addr;
405 } else {
406 f_chk_len = mid_addr +
407 frm->payload[mid_plane] - low_addr;
408 s_chk_addr = high_addr;
409 s_chk_len = frm->payload[high_plane];
412 pr_debug("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
413 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
416 int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
418 struct gsc_dev *gsc = ctx->gsc_dev;
419 struct gsc_variant *variant = gsc->variant;
420 struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
421 const struct gsc_fmt *fmt;
422 u32 max_w, max_h, mod_x, mod_y;
423 u32 min_w, min_h, tmp_w, tmp_h;
424 int i;
426 pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
428 fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
429 if (!fmt) {
430 pr_err("pixelformat format (0x%X) invalid\n",
431 pix_mp->pixelformat);
432 return -EINVAL;
435 if (pix_mp->field == V4L2_FIELD_ANY)
436 pix_mp->field = V4L2_FIELD_NONE;
437 else if (pix_mp->field != V4L2_FIELD_NONE) {
438 pr_debug("Not supported field order(%d)\n", pix_mp->field);
439 return -EINVAL;
442 max_w = variant->pix_max->target_rot_dis_w;
443 max_h = variant->pix_max->target_rot_dis_h;
445 mod_x = ffs(variant->pix_align->org_w) - 1;
446 if (is_yuv420(fmt->color))
447 mod_y = ffs(variant->pix_align->org_h) - 1;
448 else
449 mod_y = ffs(variant->pix_align->org_h) - 2;
451 if (V4L2_TYPE_IS_OUTPUT(f->type)) {
452 min_w = variant->pix_min->org_w;
453 min_h = variant->pix_min->org_h;
454 } else {
455 min_w = variant->pix_min->target_rot_dis_w;
456 min_h = variant->pix_min->target_rot_dis_h;
457 pix_mp->colorspace = ctx->out_colorspace;
460 pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
461 mod_x, mod_y, max_w, max_h);
463 /* To check if image size is modified to adjust parameter against
464 hardware abilities */
465 tmp_w = pix_mp->width;
466 tmp_h = pix_mp->height;
468 v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
469 &pix_mp->height, min_h, max_h, mod_y, 0);
470 if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
471 pr_debug("Image size has been modified from %dx%d to %dx%d\n",
472 tmp_w, tmp_h, pix_mp->width, pix_mp->height);
474 pix_mp->num_planes = fmt->num_planes;
476 if (V4L2_TYPE_IS_OUTPUT(f->type))
477 ctx->out_colorspace = pix_mp->colorspace;
479 for (i = 0; i < pix_mp->num_planes; ++i) {
480 struct v4l2_plane_pix_format *plane_fmt = &pix_mp->plane_fmt[i];
481 u32 bpl = plane_fmt->bytesperline;
483 if (fmt->num_comp == 1 && /* Packed */
484 (bpl == 0 || (bpl * 8 / fmt->depth[i]) < pix_mp->width))
485 bpl = pix_mp->width * fmt->depth[i] / 8;
487 if (fmt->num_comp > 1 && /* Planar */
488 (bpl == 0 || bpl < pix_mp->width))
489 bpl = pix_mp->width;
491 if (i != 0 && fmt->num_comp == 3)
492 bpl /= 2;
494 plane_fmt->bytesperline = bpl;
495 plane_fmt->sizeimage = max(pix_mp->width * pix_mp->height *
496 fmt->depth[i] / 8,
497 plane_fmt->sizeimage);
498 pr_debug("[%d]: bpl: %d, sizeimage: %d",
499 i, bpl, pix_mp->plane_fmt[i].sizeimage);
502 return 0;
505 int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
507 struct gsc_frame *frame;
508 struct v4l2_pix_format_mplane *pix_mp;
509 int i;
511 frame = ctx_get_frame(ctx, f->type);
512 if (IS_ERR(frame))
513 return PTR_ERR(frame);
515 pix_mp = &f->fmt.pix_mp;
517 pix_mp->width = frame->f_width;
518 pix_mp->height = frame->f_height;
519 pix_mp->field = V4L2_FIELD_NONE;
520 pix_mp->pixelformat = frame->fmt->pixelformat;
521 pix_mp->num_planes = frame->fmt->num_planes;
522 pix_mp->colorspace = ctx->out_colorspace;
524 for (i = 0; i < pix_mp->num_planes; ++i) {
525 pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
526 frame->fmt->depth[i]) / 8;
527 pix_mp->plane_fmt[i].sizeimage =
528 pix_mp->plane_fmt[i].bytesperline * frame->f_height;
531 return 0;
534 void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
536 if (tmp_w != *w || tmp_h != *h) {
537 pr_info("Cropped size has been modified from %dx%d to %dx%d",
538 *w, *h, tmp_w, tmp_h);
539 *w = tmp_w;
540 *h = tmp_h;
544 int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
546 struct gsc_frame *frame;
548 frame = ctx_get_frame(ctx, cr->type);
549 if (IS_ERR(frame))
550 return PTR_ERR(frame);
552 cr->c = frame->crop;
554 return 0;
557 int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
559 struct gsc_frame *f;
560 struct gsc_dev *gsc = ctx->gsc_dev;
561 struct gsc_variant *variant = gsc->variant;
562 u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
563 u32 min_w, min_h, max_w, max_h;
565 if (cr->c.top < 0 || cr->c.left < 0) {
566 pr_err("doesn't support negative values for top & left\n");
567 return -EINVAL;
569 pr_debug("user put w: %d, h: %d", cr->c.width, cr->c.height);
571 if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
572 f = &ctx->d_frame;
573 else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
574 f = &ctx->s_frame;
575 else
576 return -EINVAL;
578 max_w = f->f_width;
579 max_h = f->f_height;
580 tmp_w = cr->c.width;
581 tmp_h = cr->c.height;
583 if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
584 if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) ||
585 is_rgb(f->fmt->color))
586 min_w = 32;
587 else
588 min_w = 64;
589 if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) ||
590 is_yuv420(f->fmt->color))
591 min_h = 32;
592 else
593 min_h = 16;
594 } else {
595 if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
596 mod_x = ffs(variant->pix_align->target_w) - 1;
597 if (is_yuv420(f->fmt->color))
598 mod_y = ffs(variant->pix_align->target_h) - 1;
599 if (ctx->gsc_ctrls.rotate->val == 90 ||
600 ctx->gsc_ctrls.rotate->val == 270) {
601 max_w = f->f_height;
602 max_h = f->f_width;
603 min_w = variant->pix_min->target_rot_en_w;
604 min_h = variant->pix_min->target_rot_en_h;
605 tmp_w = cr->c.height;
606 tmp_h = cr->c.width;
607 } else {
608 min_w = variant->pix_min->target_rot_dis_w;
609 min_h = variant->pix_min->target_rot_dis_h;
612 pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
613 mod_x, mod_y, min_w, min_h);
614 pr_debug("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
616 v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
617 &tmp_h, min_h, max_h, mod_y, 0);
619 if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
620 (ctx->gsc_ctrls.rotate->val == 90 ||
621 ctx->gsc_ctrls.rotate->val == 270))
622 gsc_check_crop_change(tmp_h, tmp_w,
623 &cr->c.width, &cr->c.height);
624 else
625 gsc_check_crop_change(tmp_w, tmp_h,
626 &cr->c.width, &cr->c.height);
629 /* adjust left/top if cropping rectangle is out of bounds */
630 /* Need to add code to algin left value with 2's multiple */
631 if (cr->c.left + tmp_w > max_w)
632 cr->c.left = max_w - tmp_w;
633 if (cr->c.top + tmp_h > max_h)
634 cr->c.top = max_h - tmp_h;
636 if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) &&
637 cr->c.left & 1)
638 cr->c.left -= 1;
640 pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
641 cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
643 return 0;
646 int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
647 int dh, int rot, int out_path)
649 int tmp_w, tmp_h, sc_down_max;
651 if (out_path == GSC_DMA)
652 sc_down_max = var->sc_down_max;
653 else
654 sc_down_max = var->local_sc_down;
656 if (rot == 90 || rot == 270) {
657 tmp_w = dh;
658 tmp_h = dw;
659 } else {
660 tmp_w = dw;
661 tmp_h = dh;
664 if ((sw / tmp_w) > sc_down_max ||
665 (sh / tmp_h) > sc_down_max ||
666 (tmp_w / sw) > var->sc_up_max ||
667 (tmp_h / sh) > var->sc_up_max)
668 return -EINVAL;
670 return 0;
673 int gsc_set_scaler_info(struct gsc_ctx *ctx)
675 struct gsc_scaler *sc = &ctx->scaler;
676 struct gsc_frame *s_frame = &ctx->s_frame;
677 struct gsc_frame *d_frame = &ctx->d_frame;
678 struct gsc_variant *variant = ctx->gsc_dev->variant;
679 struct device *dev = &ctx->gsc_dev->pdev->dev;
680 int tx, ty;
681 int ret;
683 ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
684 s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
685 ctx->gsc_ctrls.rotate->val, ctx->out_path);
686 if (ret) {
687 pr_err("out of scaler range");
688 return ret;
691 if (ctx->gsc_ctrls.rotate->val == 90 ||
692 ctx->gsc_ctrls.rotate->val == 270) {
693 ty = d_frame->crop.width;
694 tx = d_frame->crop.height;
695 } else {
696 tx = d_frame->crop.width;
697 ty = d_frame->crop.height;
700 if (tx <= 0 || ty <= 0) {
701 dev_err(dev, "Invalid target size: %dx%d", tx, ty);
702 return -EINVAL;
705 ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
706 tx, &sc->pre_hratio);
707 if (ret) {
708 pr_err("Horizontal scale ratio is out of range");
709 return ret;
712 ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
713 ty, &sc->pre_vratio);
714 if (ret) {
715 pr_err("Vertical scale ratio is out of range");
716 return ret;
719 gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio,
720 tx, ty, &sc->pre_vratio);
722 gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
723 &sc->pre_shfactor);
725 sc->main_hratio = (s_frame->crop.width << 16) / tx;
726 sc->main_vratio = (s_frame->crop.height << 16) / ty;
728 pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
729 s_frame->crop.width, s_frame->crop.height, tx, ty);
730 pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d",
731 sc->pre_shfactor, sc->pre_hratio);
732 pr_debug("pre_v :%d, main_h : %d, main_v : %d",
733 sc->pre_vratio, sc->main_hratio, sc->main_vratio);
735 return 0;
738 static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl)
740 struct gsc_dev *gsc = ctx->gsc_dev;
741 struct gsc_variant *variant = gsc->variant;
742 unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT;
743 int ret = 0;
745 if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
746 return 0;
748 switch (ctrl->id) {
749 case V4L2_CID_HFLIP:
750 ctx->hflip = ctrl->val;
751 break;
753 case V4L2_CID_VFLIP:
754 ctx->vflip = ctrl->val;
755 break;
757 case V4L2_CID_ROTATE:
758 if ((ctx->state & flags) == flags) {
759 ret = gsc_check_scaler_ratio(variant,
760 ctx->s_frame.crop.width,
761 ctx->s_frame.crop.height,
762 ctx->d_frame.crop.width,
763 ctx->d_frame.crop.height,
764 ctx->gsc_ctrls.rotate->val,
765 ctx->out_path);
767 if (ret)
768 return -EINVAL;
771 ctx->rotation = ctrl->val;
772 break;
774 case V4L2_CID_ALPHA_COMPONENT:
775 ctx->d_frame.alpha = ctrl->val;
776 break;
779 ctx->state |= GSC_PARAMS;
780 return 0;
783 static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
785 struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
786 unsigned long flags;
787 int ret;
789 spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
790 ret = __gsc_s_ctrl(ctx, ctrl);
791 spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
793 return ret;
796 static const struct v4l2_ctrl_ops gsc_ctrl_ops = {
797 .s_ctrl = gsc_s_ctrl,
800 int gsc_ctrls_create(struct gsc_ctx *ctx)
802 if (ctx->ctrls_rdy) {
803 pr_err("Control handler of this context was created already");
804 return 0;
807 v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
809 ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
810 &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
811 ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
812 &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
813 ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
814 &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
815 ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
816 &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
818 ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
820 if (ctx->ctrl_handler.error) {
821 int err = ctx->ctrl_handler.error;
822 v4l2_ctrl_handler_free(&ctx->ctrl_handler);
823 pr_err("Failed to create G-Scaler control handlers");
824 return err;
827 return 0;
830 void gsc_ctrls_delete(struct gsc_ctx *ctx)
832 if (ctx->ctrls_rdy) {
833 v4l2_ctrl_handler_free(&ctx->ctrl_handler);
834 ctx->ctrls_rdy = false;
838 /* The color format (num_comp, num_planes) must be already configured. */
839 int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
840 struct gsc_frame *frame, struct gsc_addr *addr)
842 int ret = 0;
843 u32 pix_size;
845 if ((vb == NULL) || (frame == NULL))
846 return -EINVAL;
848 pix_size = frame->f_width * frame->f_height;
850 pr_debug("num_planes= %d, num_comp= %d, pix_size= %d",
851 frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
853 addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
855 if (frame->fmt->num_planes == 1) {
856 switch (frame->fmt->num_comp) {
857 case 1:
858 addr->cb = 0;
859 addr->cr = 0;
860 break;
861 case 2:
862 /* decompose Y into Y/Cb */
863 addr->cb = (dma_addr_t)(addr->y + pix_size);
864 addr->cr = 0;
865 break;
866 case 3:
867 /* decompose Y into Y/Cb/Cr */
868 addr->cb = (dma_addr_t)(addr->y + pix_size);
869 if (GSC_YUV420 == frame->fmt->color)
870 addr->cr = (dma_addr_t)(addr->cb
871 + (pix_size >> 2));
872 else /* 422 */
873 addr->cr = (dma_addr_t)(addr->cb
874 + (pix_size >> 1));
875 break;
876 default:
877 pr_err("Invalid the number of color planes");
878 return -EINVAL;
880 } else {
881 if (frame->fmt->num_planes >= 2)
882 addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
884 if (frame->fmt->num_planes == 3)
885 addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
888 if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) ||
889 (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) ||
890 (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) ||
891 (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
892 swap(addr->cb, addr->cr);
894 pr_debug("ADDR: y= %pad cb= %pad cr= %pad ret= %d",
895 &addr->y, &addr->cb, &addr->cr, ret);
897 return ret;
900 static irqreturn_t gsc_irq_handler(int irq, void *priv)
902 struct gsc_dev *gsc = priv;
903 struct gsc_ctx *ctx;
904 int gsc_irq;
906 gsc_irq = gsc_hw_get_irq_status(gsc);
907 gsc_hw_clear_irq(gsc, gsc_irq);
909 if (gsc_irq == GSC_IRQ_OVERRUN) {
910 pr_err("Local path input over-run interrupt has occurred!\n");
911 return IRQ_HANDLED;
914 spin_lock(&gsc->slock);
916 if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) {
918 gsc_hw_enable_control(gsc, false);
920 if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) {
921 set_bit(ST_M2M_SUSPENDED, &gsc->state);
922 wake_up(&gsc->irq_queue);
923 goto isr_unlock;
925 ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
927 if (!ctx || !ctx->m2m_ctx)
928 goto isr_unlock;
930 spin_unlock(&gsc->slock);
931 gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
933 /* wake_up job_abort, stop_streaming */
934 if (ctx->state & GSC_CTX_STOP_REQ) {
935 ctx->state &= ~GSC_CTX_STOP_REQ;
936 wake_up(&gsc->irq_queue);
938 return IRQ_HANDLED;
941 isr_unlock:
942 spin_unlock(&gsc->slock);
943 return IRQ_HANDLED;
946 static struct gsc_pix_max gsc_v_100_max = {
947 .org_scaler_bypass_w = 8192,
948 .org_scaler_bypass_h = 8192,
949 .org_scaler_input_w = 4800,
950 .org_scaler_input_h = 3344,
951 .real_rot_dis_w = 4800,
952 .real_rot_dis_h = 3344,
953 .real_rot_en_w = 2047,
954 .real_rot_en_h = 2047,
955 .target_rot_dis_w = 4800,
956 .target_rot_dis_h = 3344,
957 .target_rot_en_w = 2016,
958 .target_rot_en_h = 2016,
961 static struct gsc_pix_max gsc_v_5250_max = {
962 .org_scaler_bypass_w = 8192,
963 .org_scaler_bypass_h = 8192,
964 .org_scaler_input_w = 4800,
965 .org_scaler_input_h = 3344,
966 .real_rot_dis_w = 4800,
967 .real_rot_dis_h = 3344,
968 .real_rot_en_w = 2016,
969 .real_rot_en_h = 2016,
970 .target_rot_dis_w = 4800,
971 .target_rot_dis_h = 3344,
972 .target_rot_en_w = 2016,
973 .target_rot_en_h = 2016,
976 static struct gsc_pix_max gsc_v_5420_max = {
977 .org_scaler_bypass_w = 8192,
978 .org_scaler_bypass_h = 8192,
979 .org_scaler_input_w = 4800,
980 .org_scaler_input_h = 3344,
981 .real_rot_dis_w = 4800,
982 .real_rot_dis_h = 3344,
983 .real_rot_en_w = 2048,
984 .real_rot_en_h = 2048,
985 .target_rot_dis_w = 4800,
986 .target_rot_dis_h = 3344,
987 .target_rot_en_w = 2016,
988 .target_rot_en_h = 2016,
991 static struct gsc_pix_max gsc_v_5433_max = {
992 .org_scaler_bypass_w = 8192,
993 .org_scaler_bypass_h = 8192,
994 .org_scaler_input_w = 4800,
995 .org_scaler_input_h = 3344,
996 .real_rot_dis_w = 4800,
997 .real_rot_dis_h = 3344,
998 .real_rot_en_w = 2047,
999 .real_rot_en_h = 2047,
1000 .target_rot_dis_w = 4800,
1001 .target_rot_dis_h = 3344,
1002 .target_rot_en_w = 2016,
1003 .target_rot_en_h = 2016,
1006 static struct gsc_pix_min gsc_v_100_min = {
1007 .org_w = 64,
1008 .org_h = 32,
1009 .real_w = 64,
1010 .real_h = 32,
1011 .target_rot_dis_w = 64,
1012 .target_rot_dis_h = 32,
1013 .target_rot_en_w = 32,
1014 .target_rot_en_h = 16,
1017 static struct gsc_pix_align gsc_v_100_align = {
1018 .org_h = 16,
1019 .org_w = 16, /* yuv420 : 16, others : 8 */
1020 .offset_h = 2, /* yuv420/422 : 2, others : 1 */
1021 .real_w = 16, /* yuv420/422 : 4~16, others : 2~8 */
1022 .real_h = 16, /* yuv420 : 4~16, others : 1 */
1023 .target_w = 2, /* yuv420/422 : 2, others : 1 */
1024 .target_h = 2, /* yuv420 : 2, others : 1 */
1027 static struct gsc_variant gsc_v_100_variant = {
1028 .pix_max = &gsc_v_100_max,
1029 .pix_min = &gsc_v_100_min,
1030 .pix_align = &gsc_v_100_align,
1031 .in_buf_cnt = 32,
1032 .out_buf_cnt = 32,
1033 .sc_up_max = 8,
1034 .sc_down_max = 16,
1035 .poly_sc_down_max = 4,
1036 .pre_sc_down_max = 4,
1037 .local_sc_down = 2,
1040 static struct gsc_variant gsc_v_5250_variant = {
1041 .pix_max = &gsc_v_5250_max,
1042 .pix_min = &gsc_v_100_min,
1043 .pix_align = &gsc_v_100_align,
1044 .in_buf_cnt = 32,
1045 .out_buf_cnt = 32,
1046 .sc_up_max = 8,
1047 .sc_down_max = 16,
1048 .poly_sc_down_max = 4,
1049 .pre_sc_down_max = 4,
1050 .local_sc_down = 2,
1053 static struct gsc_variant gsc_v_5420_variant = {
1054 .pix_max = &gsc_v_5420_max,
1055 .pix_min = &gsc_v_100_min,
1056 .pix_align = &gsc_v_100_align,
1057 .in_buf_cnt = 32,
1058 .out_buf_cnt = 32,
1059 .sc_up_max = 8,
1060 .sc_down_max = 16,
1061 .poly_sc_down_max = 4,
1062 .pre_sc_down_max = 4,
1063 .local_sc_down = 2,
1066 static struct gsc_variant gsc_v_5433_variant = {
1067 .pix_max = &gsc_v_5433_max,
1068 .pix_min = &gsc_v_100_min,
1069 .pix_align = &gsc_v_100_align,
1070 .in_buf_cnt = 32,
1071 .out_buf_cnt = 32,
1072 .sc_up_max = 8,
1073 .sc_down_max = 16,
1074 .poly_sc_down_max = 4,
1075 .pre_sc_down_max = 4,
1076 .local_sc_down = 2,
1079 static struct gsc_driverdata gsc_v_100_drvdata = {
1080 .variant = {
1081 [0] = &gsc_v_100_variant,
1082 [1] = &gsc_v_100_variant,
1083 [2] = &gsc_v_100_variant,
1084 [3] = &gsc_v_100_variant,
1086 .num_entities = 4,
1087 .clk_names = { "gscl" },
1088 .num_clocks = 1,
1091 static struct gsc_driverdata gsc_v_5250_drvdata = {
1092 .variant = {
1093 [0] = &gsc_v_5250_variant,
1094 [1] = &gsc_v_5250_variant,
1095 [2] = &gsc_v_5250_variant,
1096 [3] = &gsc_v_5250_variant,
1098 .num_entities = 4,
1099 .clk_names = { "gscl" },
1100 .num_clocks = 1,
1103 static struct gsc_driverdata gsc_v_5420_drvdata = {
1104 .variant = {
1105 [0] = &gsc_v_5420_variant,
1106 [1] = &gsc_v_5420_variant,
1108 .num_entities = 2,
1109 .clk_names = { "gscl" },
1110 .num_clocks = 1,
1113 static struct gsc_driverdata gsc_5433_drvdata = {
1114 .variant = {
1115 [0] = &gsc_v_5433_variant,
1116 [1] = &gsc_v_5433_variant,
1117 [2] = &gsc_v_5433_variant,
1119 .num_entities = 3,
1120 .clk_names = { "pclk", "aclk", "aclk_xiu", "aclk_gsclbend" },
1121 .num_clocks = 4,
1124 static const struct of_device_id exynos_gsc_match[] = {
1126 .compatible = "samsung,exynos5250-gsc",
1127 .data = &gsc_v_5250_drvdata,
1130 .compatible = "samsung,exynos5420-gsc",
1131 .data = &gsc_v_5420_drvdata,
1134 .compatible = "samsung,exynos5433-gsc",
1135 .data = &gsc_5433_drvdata,
1138 .compatible = "samsung,exynos5-gsc",
1139 .data = &gsc_v_100_drvdata,
1143 MODULE_DEVICE_TABLE(of, exynos_gsc_match);
1145 static int gsc_probe(struct platform_device *pdev)
1147 struct gsc_dev *gsc;
1148 struct resource *res;
1149 struct device *dev = &pdev->dev;
1150 const struct gsc_driverdata *drv_data = of_device_get_match_data(dev);
1151 int ret;
1152 int i;
1154 gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL);
1155 if (!gsc)
1156 return -ENOMEM;
1158 ret = of_alias_get_id(pdev->dev.of_node, "gsc");
1159 if (ret < 0)
1160 return ret;
1162 if (drv_data == &gsc_v_100_drvdata)
1163 dev_info(dev, "compatible 'exynos5-gsc' is deprecated\n");
1165 gsc->id = ret;
1166 if (gsc->id >= drv_data->num_entities) {
1167 dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
1168 return -EINVAL;
1171 gsc->num_clocks = drv_data->num_clocks;
1172 gsc->variant = drv_data->variant[gsc->id];
1173 gsc->pdev = pdev;
1175 init_waitqueue_head(&gsc->irq_queue);
1176 spin_lock_init(&gsc->slock);
1177 mutex_init(&gsc->lock);
1179 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1180 gsc->regs = devm_ioremap_resource(dev, res);
1181 if (IS_ERR(gsc->regs))
1182 return PTR_ERR(gsc->regs);
1184 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
1185 if (!res) {
1186 dev_err(dev, "failed to get IRQ resource\n");
1187 return -ENXIO;
1190 for (i = 0; i < gsc->num_clocks; i++) {
1191 gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]);
1192 if (IS_ERR(gsc->clock[i])) {
1193 dev_err(dev, "failed to get clock: %s\n",
1194 drv_data->clk_names[i]);
1195 return PTR_ERR(gsc->clock[i]);
1199 for (i = 0; i < gsc->num_clocks; i++) {
1200 ret = clk_prepare_enable(gsc->clock[i]);
1201 if (ret) {
1202 dev_err(dev, "clock prepare failed for clock: %s\n",
1203 drv_data->clk_names[i]);
1204 while (--i >= 0)
1205 clk_disable_unprepare(gsc->clock[i]);
1206 return ret;
1210 ret = devm_request_irq(dev, res->start, gsc_irq_handler,
1211 0, pdev->name, gsc);
1212 if (ret) {
1213 dev_err(dev, "failed to install irq (%d)\n", ret);
1214 goto err_clk;
1217 ret = v4l2_device_register(dev, &gsc->v4l2_dev);
1218 if (ret)
1219 goto err_clk;
1221 ret = gsc_register_m2m_device(gsc);
1222 if (ret)
1223 goto err_v4l2;
1225 platform_set_drvdata(pdev, gsc);
1227 gsc_hw_set_sw_reset(gsc);
1228 gsc_wait_reset(gsc);
1230 vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
1232 dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id);
1234 pm_runtime_set_active(dev);
1235 pm_runtime_enable(dev);
1237 return 0;
1239 err_v4l2:
1240 v4l2_device_unregister(&gsc->v4l2_dev);
1241 err_clk:
1242 for (i = gsc->num_clocks - 1; i >= 0; i--)
1243 clk_disable_unprepare(gsc->clock[i]);
1244 return ret;
1247 static int gsc_remove(struct platform_device *pdev)
1249 struct gsc_dev *gsc = platform_get_drvdata(pdev);
1250 int i;
1252 pm_runtime_get_sync(&pdev->dev);
1254 gsc_unregister_m2m_device(gsc);
1255 v4l2_device_unregister(&gsc->v4l2_dev);
1257 vb2_dma_contig_clear_max_seg_size(&pdev->dev);
1258 for (i = 0; i < gsc->num_clocks; i++)
1259 clk_disable_unprepare(gsc->clock[i]);
1261 pm_runtime_put_noidle(&pdev->dev);
1262 pm_runtime_disable(&pdev->dev);
1264 dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
1265 return 0;
1268 #ifdef CONFIG_PM
1269 static int gsc_m2m_suspend(struct gsc_dev *gsc)
1271 unsigned long flags;
1272 int timeout;
1274 spin_lock_irqsave(&gsc->slock, flags);
1275 if (!gsc_m2m_pending(gsc)) {
1276 spin_unlock_irqrestore(&gsc->slock, flags);
1277 return 0;
1279 clear_bit(ST_M2M_SUSPENDED, &gsc->state);
1280 set_bit(ST_M2M_SUSPENDING, &gsc->state);
1281 spin_unlock_irqrestore(&gsc->slock, flags);
1283 timeout = wait_event_timeout(gsc->irq_queue,
1284 test_bit(ST_M2M_SUSPENDED, &gsc->state),
1285 GSC_SHUTDOWN_TIMEOUT);
1287 clear_bit(ST_M2M_SUSPENDING, &gsc->state);
1288 return timeout == 0 ? -EAGAIN : 0;
1291 static void gsc_m2m_resume(struct gsc_dev *gsc)
1293 struct gsc_ctx *ctx;
1294 unsigned long flags;
1296 spin_lock_irqsave(&gsc->slock, flags);
1297 /* Clear for full H/W setup in first run after resume */
1298 ctx = gsc->m2m.ctx;
1299 gsc->m2m.ctx = NULL;
1300 spin_unlock_irqrestore(&gsc->slock, flags);
1302 if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state))
1303 gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
1306 static int gsc_runtime_resume(struct device *dev)
1308 struct gsc_dev *gsc = dev_get_drvdata(dev);
1309 int ret = 0;
1310 int i;
1312 pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state);
1314 for (i = 0; i < gsc->num_clocks; i++) {
1315 ret = clk_prepare_enable(gsc->clock[i]);
1316 if (ret) {
1317 while (--i >= 0)
1318 clk_disable_unprepare(gsc->clock[i]);
1319 return ret;
1323 gsc_hw_set_sw_reset(gsc);
1324 gsc_wait_reset(gsc);
1325 gsc_m2m_resume(gsc);
1327 return 0;
1330 static int gsc_runtime_suspend(struct device *dev)
1332 struct gsc_dev *gsc = dev_get_drvdata(dev);
1333 int ret = 0;
1334 int i;
1336 ret = gsc_m2m_suspend(gsc);
1337 if (ret)
1338 return ret;
1340 for (i = gsc->num_clocks - 1; i >= 0; i--)
1341 clk_disable_unprepare(gsc->clock[i]);
1343 pr_debug("gsc%d: state: 0x%lx\n", gsc->id, gsc->state);
1344 return ret;
1346 #endif
1348 static const struct dev_pm_ops gsc_pm_ops = {
1349 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1350 pm_runtime_force_resume)
1351 SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL)
1354 static struct platform_driver gsc_driver = {
1355 .probe = gsc_probe,
1356 .remove = gsc_remove,
1357 .driver = {
1358 .name = GSC_MODULE_NAME,
1359 .pm = &gsc_pm_ops,
1360 .of_match_table = exynos_gsc_match,
1364 module_platform_driver(gsc_driver);
1366 MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@samsung.com>");
1367 MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
1368 MODULE_LICENSE("GPL");