1 /*
2 * linux-5.4/drivers/media/platform/sunxi-vin/vin-vipp/sunxi_scaler.c
3 *
4 * Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17 #include <linux/platform_device.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <media/v4l2-device.h>
21 #include <media/v4l2-mediabus.h>
22 #include <media/v4l2-subdev.h>
23 #include "../platform/platform_cfg.h"
24 #include "sunxi_scaler.h"
25 #include "../vin-video/vin_core.h"
26 #include "vipp_reg.h"
27
28 #define SCALER_MODULE_NAME "vin_scaler"
29
30 struct scaler_dev *glb_vipp[VIN_MAX_SCALER];
31
32 #define MIN_IN_WIDTH 192
33 #define MIN_IN_HEIGHT 128
34 #define MAX_IN_WIDTH 4224
35 #define MAX_IN_HEIGHT 4224
36
37 #define MIN_OUT_WIDTH 16
38 #define MIN_OUT_HEIGHT 10
39 #define MAX_OUT_WIDTH 4224
40 #define MAX_OUT_HEIGHT 4224
41
42 #define MIN_RATIO 256
43 #if !defined CONFIG_ARCH_SUN8IW19P1 && !defined CONFIG_ARCH_SUN50IW10
44 #define MAX_RATIO 2048
45 #else
46 #define MAX_RATIO 4096
47 #endif
48
__scaler_try_crop(const struct v4l2_mbus_framefmt * sink,const struct v4l2_mbus_framefmt * source,struct v4l2_rect * crop)49 static void __scaler_try_crop(const struct v4l2_mbus_framefmt *sink,
50 const struct v4l2_mbus_framefmt *source,
51 struct v4l2_rect *crop)
52 {
53 /* Crop can not go beyond of the input rectangle */
54 crop->left = clamp_t(u32, crop->left, 0, sink->width - source->width);
55 crop->width = clamp_t(u32, crop->width, source->width, sink->width - crop->left);
56 crop->top = clamp_t(u32, crop->top, 0, sink->height - source->height);
57 crop->height = clamp_t(u32, crop->height, source->height, sink->height - crop->top);
58 }
59
__scaler_w_shift(int x_ratio,int y_ratio)60 static int __scaler_w_shift(int x_ratio, int y_ratio)
61 {
62 int m, n;
63 int sum_weight = 0;
64 int weight_shift;
65 int xr = (x_ratio >> 8) + 1;
66 int yr = (y_ratio >> 8) + 1;
67
68 #if defined CONFIG_ARCH_SUN8IW19P1 || defined CONFIG_ARCH_SUN50IW10
69 int weight_shift_bak = 0;
70
71 weight_shift = -9;
72 for (weight_shift = 17; weight_shift > 0; weight_shift--) {
73 sum_weight = 0;
74 for (m = 0; m <= xr; m++) {
75 for (n = 0; n <= yr; n++) {
76 sum_weight += (y_ratio - abs((n << 8) - (yr << 7)))*
77 (x_ratio - abs((m << 8) - (yr << 7))) >> (weight_shift + 8);
78 }
79 }
80 if (sum_weight > 0 && sum_weight < 256)
81 weight_shift_bak = weight_shift;
82 if (sum_weight > 255 && sum_weight < 486)
83 break;
84 }
85 if (weight_shift == 0)
86 weight_shift = weight_shift_bak;
87 #else
88 weight_shift = -8;
89 for (m = 0; m <= xr; m++) {
90 for (n = 0; n <= yr; n++) {
91 sum_weight += (y_ratio - abs((n << 8) - (yr << 7)))
92 * (x_ratio - abs((m << 8) - (xr << 7)));
93 }
94 }
95 sum_weight >>= 8;
96 while (sum_weight != 0) {
97 weight_shift++;
98 sum_weight >>= 1;
99 }
100 #endif
101 return weight_shift;
102 }
103
__scaler_calc_ratios(struct scaler_dev * scaler,struct v4l2_rect * input,struct v4l2_mbus_framefmt * output,struct scaler_para * para)104 static void __scaler_calc_ratios(struct scaler_dev *scaler,
105 struct v4l2_rect *input,
106 struct v4l2_mbus_framefmt *output,
107 struct scaler_para *para)
108 {
109 unsigned int width;
110 unsigned int height;
111 unsigned int r_min;
112
113 output->width = clamp_t(u32, output->width, MIN_OUT_WIDTH, input->width);
114 output->height =
115 clamp_t(u32, output->height, MIN_OUT_HEIGHT, input->height);
116
117 para->xratio = 256 * input->width / output->width;
118 para->yratio = 256 * input->height / output->height;
119 para->xratio = clamp_t(u32, para->xratio, MIN_RATIO, MAX_RATIO);
120 para->yratio = clamp_t(u32, para->yratio, MIN_RATIO, MAX_RATIO);
121
122 r_min = min(para->xratio, para->yratio);
123 #ifdef CROP_AFTER_SCALER
124 width = ALIGN(256 * input->width / r_min, 4);
125 height = ALIGN(256 * input->height / r_min, 2);
126 para->xratio = 256 * input->width / width;
127 para->yratio = 256 * input->height / height;
128 para->xratio = clamp_t(u32, para->xratio, MIN_RATIO, MAX_RATIO);
129 para->yratio = clamp_t(u32, para->yratio, MIN_RATIO, MAX_RATIO);
130 para->width = width;
131 para->height = height;
132 vin_log(VIN_LOG_SCALER, "para: xr = %d, yr = %d, w = %d, h = %d\n",
133 para->xratio, para->yratio, para->width, para->height);
134
135 output->width = width;
136 output->height = height;
137 #else
138 width = ALIGN(output->width * r_min / 256, 4);
139 height = ALIGN(output->height * r_min / 256, 2);
140 para->xratio = 256 * width / output->width;
141 para->yratio = 256 * height / output->height;
142 para->xratio = clamp_t(u32, para->xratio, MIN_RATIO, MAX_RATIO);
143 para->yratio = clamp_t(u32, para->yratio, MIN_RATIO, MAX_RATIO);
144 para->width = output->width;
145 para->height = output->height;
146 vin_log(VIN_LOG_SCALER, "para: xr = %d, yr = %d, w = %d, h = %d\n",
147 para->xratio, para->yratio, para->width, para->height);
148 /* Center the new crop rectangle.
149 * crop is before scaler
150 */
151 input->left += (input->width - width) / 2;
152 input->top += (input->height - height) / 2;
153 input->left = ALIGN(input->left, 4);
154 input->top = ALIGN(input->top, 2);
155 input->width = width;
156 input->height = height;
157 #endif
158 vin_log(VIN_LOG_SCALER, "crop: left = %d, top = %d, w = %d, h = %d\n",
159 input->left, input->top, input->width, input->height);
160 }
161
__scaler_try_format(struct scaler_dev * scaler,struct v4l2_subdev_pad_config * cfg,unsigned int pad,struct v4l2_mbus_framefmt * fmt,enum v4l2_subdev_format_whence which)162 static void __scaler_try_format(struct scaler_dev *scaler,
163 struct v4l2_subdev_pad_config *cfg, unsigned int pad,
164 struct v4l2_mbus_framefmt *fmt,
165 enum v4l2_subdev_format_whence which)
166 {
167 struct scaler_para ratio;
168
169 switch (pad) {
170 case SCALER_PAD_SINK:
171 fmt->width =
172 clamp_t(u32, fmt->width, MIN_IN_WIDTH, MAX_IN_WIDTH);
173 fmt->height =
174 clamp_t(u32, fmt->height, MIN_IN_HEIGHT, MAX_IN_HEIGHT);
175 break;
176 case SCALER_PAD_SOURCE:
177 fmt->code = scaler->formats[SCALER_PAD_SINK].code;
178 __scaler_calc_ratios(scaler, &scaler->crop.request, fmt, &ratio);
179 break;
180 }
181 }
182
sunxi_scaler_subdev_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)183 static int sunxi_scaler_subdev_get_fmt(struct v4l2_subdev *sd,
184 struct v4l2_subdev_pad_config *cfg,
185 struct v4l2_subdev_format *fmt)
186 {
187 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
188
189 fmt->format = scaler->formats[fmt->pad];
190
191 return 0;
192 }
193
sunxi_scaler_subdev_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_format * fmt)194 static int sunxi_scaler_subdev_set_fmt(struct v4l2_subdev *sd,
195 struct v4l2_subdev_pad_config *cfg,
196 struct v4l2_subdev_format *fmt)
197 {
198 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
199 struct v4l2_mbus_framefmt *format = &scaler->formats[fmt->pad];
200
201 vin_log(VIN_LOG_FMT, "%s %d*%d %x %d\n", __func__, fmt->format.width,
202 fmt->format.height, fmt->format.code, fmt->format.field);
203 __scaler_try_format(scaler, cfg, fmt->pad, &fmt->format, fmt->which);
204 *format = fmt->format;
205
206 if (fmt->pad == SCALER_PAD_SINK) {
207 /* reset crop rectangle */
208 scaler->crop.request.left = 0;
209 scaler->crop.request.top = 0;
210 scaler->crop.request.width = fmt->format.width;
211 scaler->crop.request.height = fmt->format.height;
212
213 /* Propagate the format from sink to source */
214 format = &scaler->formats[SCALER_PAD_SOURCE];
215 *format = fmt->format;
216 __scaler_try_format(scaler, cfg, SCALER_PAD_SOURCE, format,
217 fmt->which);
218 }
219
220 if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
221 scaler->crop.active = scaler->crop.request;
222 __scaler_calc_ratios(scaler, &scaler->crop.active, format,
223 &scaler->para);
224 }
225
226 /*return the format for other subdev*/
227 fmt->format = *format;
228
229 return 0;
230 }
231
sunxi_scaler_subdev_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)232 static int sunxi_scaler_subdev_get_selection(struct v4l2_subdev *sd,
233 struct v4l2_subdev_pad_config *cfg,
234 struct v4l2_subdev_selection *sel)
235 {
236 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
237 struct v4l2_mbus_framefmt *format_source;
238 struct v4l2_mbus_framefmt *format_sink;
239
240 vin_log(VIN_LOG_SCALER, "%s\n", __func__);
241
242 if (sel->pad != SCALER_PAD_SINK)
243 return -EINVAL;
244
245 format_sink = &scaler->formats[SCALER_PAD_SINK];
246 format_source = &scaler->formats[SCALER_PAD_SOURCE];
247
248 switch (sel->target) {
249 case V4L2_SEL_TGT_CROP_BOUNDS:
250 sel->r.left = 0;
251 sel->r.top = 0;
252 sel->r.width = INT_MAX;
253 sel->r.height = INT_MAX;
254 __scaler_try_crop(format_sink, format_source, &sel->r);
255 break;
256 case V4L2_SEL_TGT_CROP:
257 sel->r = scaler->crop.request;
258 break;
259 default:
260 return -EINVAL;
261 }
262
263 return 0;
264 }
265
sunxi_scaler_subdev_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_pad_config * cfg,struct v4l2_subdev_selection * sel)266 static int sunxi_scaler_subdev_set_selection(struct v4l2_subdev *sd,
267 struct v4l2_subdev_pad_config *cfg,
268 struct v4l2_subdev_selection *sel)
269 {
270 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
271 struct v4l2_mbus_framefmt *format_sink, *format_source;
272 struct scaler_para para;
273 struct vipp_scaler_config scaler_cfg;
274 struct vipp_crop crop;
275
276 if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != SCALER_PAD_SINK)
277 return -EINVAL;
278
279 format_sink = &scaler->formats[SCALER_PAD_SINK];
280 format_source = &scaler->formats[SCALER_PAD_SOURCE];
281
282 vin_log(VIN_LOG_FMT, "%s: L = %d, T = %d, W = %d, H = %d\n", __func__,
283 sel->r.left, sel->r.top, sel->r.width, sel->r.height);
284
285 vin_log(VIN_LOG_FMT, "%s: input = %dx%d, output = %dx%d\n", __func__,
286 format_sink->width, format_sink->height,
287 format_source->width, format_source->height);
288
289 __scaler_try_crop(format_sink, format_source, &sel->r);
290
291 if (sel->reserved[0] != VIPP_ONLY_SHRINK) { /* vipp crop */
292 __scaler_calc_ratios(scaler, &sel->r, format_source, ¶);
293
294 if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
295 return 0;
296
297 scaler->para = para;
298 scaler->crop.active = sel->r;
299 scaler->crop.request = sel->r;
300
301 if (sd->entity.stream_count)
302 vipp_set_para_ready(scaler->id, NOT_READY);
303 /* we need update register when streaming */
304 crop.hor = scaler->crop.active.left;
305 crop.ver = scaler->crop.active.top;
306 crop.width = scaler->crop.active.width;
307 crop.height = scaler->crop.active.height;
308 vipp_set_crop(scaler->id, &crop);
309
310 if (scaler->id < MAX_OSD_NUM)
311 scaler_cfg.sc_out_fmt = YUV422;
312 else
313 scaler_cfg.sc_out_fmt = YUV420;
314 scaler_cfg.sc_x_ratio = scaler->para.xratio;
315 scaler_cfg.sc_y_ratio = scaler->para.yratio;
316 scaler_cfg.sc_w_shift = __scaler_w_shift(scaler->para.xratio, scaler->para.yratio);
317 vipp_scaler_cfg(scaler->id, &scaler_cfg);
318
319 if (sd->entity.stream_count)
320 vipp_set_para_ready(scaler->id, HAS_READY);
321 vin_log(VIN_LOG_SCALER, "active crop: l = %d, t = %d, w = %d, h = %d\n",
322 scaler->crop.active.left, scaler->crop.active.top,
323 scaler->crop.active.width, scaler->crop.active.height);
324 } else { /* vipp shrink */
325 if ((sel->r.width != format_source->width) || (sel->r.height != format_source->height)) {
326 vin_err("vipp shrink size is not equal to output size");
327 return -EINVAL;
328 }
329
330 scaler->crop.active.left = sel->r.left;
331 scaler->crop.active.top = sel->r.top;
332 scaler->crop.active.width = format_sink->width;
333 scaler->crop.active.height = format_sink->height;
334 scaler->para.xratio = scaler->crop.active.width * 256 / sel->r.width;
335 scaler->para.yratio = scaler->crop.active.height * 256 / sel->r.height;
336
337 vin_log(VIN_LOG_SCALER, "active shrink: l = %d, t = %d, w = %d, h = %d, xratio = %d, yratio = %d\n",
338 scaler->crop.active.left, scaler->crop.active.top,
339 scaler->crop.active.width, scaler->crop.active.height,
340 scaler->para.xratio, scaler->para.yratio);
341 }
342 return 0;
343 }
344
sunxi_scaler_subdev_init(struct v4l2_subdev * sd,u32 val)345 int sunxi_scaler_subdev_init(struct v4l2_subdev *sd, u32 val)
346 {
347 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
348
349 vin_log(VIN_LOG_SCALER, "%s, val = %d.\n", __func__, val);
350 if (val) {
351 memset(scaler->vipp_reg.vir_addr, 0, scaler->vipp_reg.size);
352 vipp_set_reg_load_addr(scaler->id, (unsigned long)scaler->vipp_reg.dma_addr);
353 vipp_set_osd_para_load_addr(scaler->id, (unsigned long)scaler->osd_para.dma_addr);
354 vipp_set_osd_stat_load_addr(scaler->id, (unsigned long)scaler->osd_stat.dma_addr);
355 vipp_set_osd_cv_update(scaler->id, NOT_UPDATED);
356 vipp_set_osd_ov_update(scaler->id, NOT_UPDATED);
357 vipp_set_para_ready(scaler->id, NOT_READY);
358 }
359 return 0;
360 }
361
sunxi_scaler_subdev_s_stream(struct v4l2_subdev * sd,int enable)362 static int sunxi_scaler_subdev_s_stream(struct v4l2_subdev *sd, int enable)
363 {
364 struct scaler_dev *scaler = v4l2_get_subdevdata(sd);
365 struct v4l2_mbus_framefmt *mf = &scaler->formats[SCALER_PAD_SOURCE];
366 struct mbus_framefmt_res *res = (void *)scaler->formats[SCALER_PAD_SOURCE].reserved;
367 struct vipp_scaler_config scaler_cfg;
368 struct vipp_scaler_size scaler_size;
369 struct vipp_crop crop;
370 enum vipp_format out_fmt;
371 enum vipp_format sc_fmt;
372
373 switch (res->res_pix_fmt) {
374 case V4L2_PIX_FMT_SBGGR8:
375 case V4L2_PIX_FMT_SGBRG8:
376 case V4L2_PIX_FMT_SGRBG8:
377 case V4L2_PIX_FMT_SRGGB8:
378 case V4L2_PIX_FMT_SBGGR10:
379 case V4L2_PIX_FMT_SGBRG10:
380 case V4L2_PIX_FMT_SGRBG10:
381 case V4L2_PIX_FMT_SRGGB10:
382 case V4L2_PIX_FMT_SBGGR12:
383 case V4L2_PIX_FMT_SGBRG12:
384 case V4L2_PIX_FMT_SGRBG12:
385 case V4L2_PIX_FMT_SRGGB12:
386 vin_log(VIN_LOG_FMT, "%s output fmt is raw, return directly\n", __func__);
387 return 0;
388 default:
389 break;
390 }
391 if (mf->field == V4L2_FIELD_INTERLACED || mf->field == V4L2_FIELD_TOP ||
392 mf->field == V4L2_FIELD_BOTTOM) {
393 vin_log(VIN_LOG_SCALER, "Scaler not support field mode, return directly!\n");
394 return 0;
395 }
396
397 if (enable) {
398 crop.hor = scaler->crop.active.left;
399 crop.ver = scaler->crop.active.top;
400 crop.width = scaler->crop.active.width;
401 crop.height = scaler->crop.active.height;
402 vipp_set_crop(scaler->id, &crop);
403 scaler_size.sc_width = scaler->para.width;
404 scaler_size.sc_height = scaler->para.height;
405 vipp_scaler_output_size(scaler->id, &scaler_size);
406
407 switch (res->res_pix_fmt) {
408 case V4L2_PIX_FMT_YUV420:
409 case V4L2_PIX_FMT_YUV420M:
410 case V4L2_PIX_FMT_YVU420:
411 case V4L2_PIX_FMT_YVU420M:
412 case V4L2_PIX_FMT_NV21:
413 case V4L2_PIX_FMT_NV21M:
414 case V4L2_PIX_FMT_NV12:
415 case V4L2_PIX_FMT_NV12M:
416 case V4L2_PIX_FMT_FBC:
417 case V4L2_PIX_FMT_LBC_2_0X:
418 case V4L2_PIX_FMT_LBC_2_5X:
419 case V4L2_PIX_FMT_LBC_1_0X:
420 if (scaler->id < MAX_OSD_NUM) {
421 sc_fmt = YUV422;
422 out_fmt = YUV420;
423 vipp_chroma_ds_en(scaler->id, 1);
424 } else {
425 sc_fmt = YUV420;
426 out_fmt = YUV420;
427 }
428 break;
429 case V4L2_PIX_FMT_YUV422P:
430 case V4L2_PIX_FMT_NV16:
431 case V4L2_PIX_FMT_NV61:
432 case V4L2_PIX_FMT_NV61M:
433 case V4L2_PIX_FMT_NV16M:
434 sc_fmt = YUV422;
435 out_fmt = YUV422;
436 break;
437 default:
438 sc_fmt = YUV420;
439 out_fmt = YUV420;
440 break;
441 }
442 scaler_cfg.sc_out_fmt = sc_fmt;
443 scaler_cfg.sc_x_ratio = scaler->para.xratio;
444 scaler_cfg.sc_y_ratio = scaler->para.yratio;
445 scaler_cfg.sc_w_shift = __scaler_w_shift(scaler->para.xratio, scaler->para.yratio);
446 vipp_scaler_cfg(scaler->id, &scaler_cfg);
447 vipp_output_fmt_cfg(scaler->id, out_fmt);
448 vipp_scaler_en(scaler->id, 1);
449 vipp_osd_en(scaler->id, 1);
450 vipp_set_para_ready(scaler->id, HAS_READY);
451 vipp_set_osd_ov_update(scaler->id, HAS_UPDATED);
452 vipp_set_osd_cv_update(scaler->id, HAS_UPDATED);
453 vipp_top_clk_en(scaler->id, enable);
454 vipp_enable(scaler->id);
455 } else {
456 vipp_disable(scaler->id);
457 vipp_top_clk_en(scaler->id, enable);
458 vipp_chroma_ds_en(scaler->id, 0);
459 vipp_osd_en(scaler->id, 0);
460 vipp_scaler_en(scaler->id, 0);
461 vipp_set_para_ready(scaler->id, NOT_READY);
462 vipp_set_osd_ov_update(scaler->id, NOT_UPDATED);
463 vipp_set_osd_cv_update(scaler->id, NOT_UPDATED);
464 }
465 vin_log(VIN_LOG_FMT, "vipp%d %s, %d*%d hoff: %d voff: %d xr: %d yr: %d\n",
466 scaler->id, enable ? "stream on" : "stream off",
467 scaler->para.width, scaler->para.height,
468 scaler->crop.active.left, scaler->crop.active.top,
469 scaler->para.xratio, scaler->para.yratio);
470
471 return 0;
472 }
473
474 static const struct v4l2_subdev_core_ops sunxi_scaler_subdev_core_ops = {
475 .init = sunxi_scaler_subdev_init,
476 };
477 static const struct v4l2_subdev_video_ops sunxi_scaler_subdev_video_ops = {
478 .s_stream = sunxi_scaler_subdev_s_stream,
479 };
480 /* subdev pad operations */
481 static const struct v4l2_subdev_pad_ops sunxi_scaler_subdev_pad_ops = {
482 .get_fmt = sunxi_scaler_subdev_get_fmt,
483 .set_fmt = sunxi_scaler_subdev_set_fmt,
484 .get_selection = sunxi_scaler_subdev_get_selection,
485 .set_selection = sunxi_scaler_subdev_set_selection,
486 };
487
488 static struct v4l2_subdev_ops sunxi_scaler_subdev_ops = {
489 .core = &sunxi_scaler_subdev_core_ops,
490 .video = &sunxi_scaler_subdev_video_ops,
491 .pad = &sunxi_scaler_subdev_pad_ops,
492 };
493
__scaler_init_subdev(struct scaler_dev * scaler)494 int __scaler_init_subdev(struct scaler_dev *scaler)
495 {
496 int ret;
497 struct v4l2_subdev *sd = &scaler->subdev;
498
499 mutex_init(&scaler->subdev_lock);
500
501 v4l2_subdev_init(sd, &sunxi_scaler_subdev_ops);
502 sd->grp_id = VIN_GRP_ID_SCALER;
503 sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
504 snprintf(sd->name, sizeof(sd->name), "sunxi_scaler.%u", scaler->id);
505 v4l2_set_subdevdata(sd, scaler);
506
507 /*sd->entity->ops = &isp_media_ops;*/
508 scaler->scaler_pads[SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
509 scaler->scaler_pads[SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
510 sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
511
512 ret = media_entity_pads_init(&sd->entity, SCALER_PAD_NUM,
513 scaler->scaler_pads);
514 if (ret < 0)
515 return ret;
516 return 0;
517 }
518
scaler_resource_alloc(struct scaler_dev * scaler)519 static int scaler_resource_alloc(struct scaler_dev *scaler)
520 {
521 int ret = 0;
522
523 scaler->vipp_reg.size = VIPP_REG_SIZE + OSD_PARA_SIZE + OSD_STAT_SIZE;
524
525 ret = os_mem_alloc(&scaler->pdev->dev, &scaler->vipp_reg);
526 if (ret < 0) {
527 vin_err("scaler regs load addr requset failed!\n");
528 return -ENOMEM;
529 }
530
531 scaler->osd_para.dma_addr = scaler->vipp_reg.dma_addr + VIPP_REG_SIZE;
532 scaler->osd_para.vir_addr = scaler->vipp_reg.vir_addr + VIPP_REG_SIZE;
533 scaler->osd_stat.dma_addr = scaler->osd_para.dma_addr + OSD_PARA_SIZE;
534 scaler->osd_stat.vir_addr = scaler->osd_para.vir_addr + OSD_PARA_SIZE;
535
536 return 0;
537 }
538
scaler_resource_free(struct scaler_dev * scaler)539 static void scaler_resource_free(struct scaler_dev *scaler)
540 {
541 os_mem_free(&scaler->pdev->dev, &scaler->vipp_reg);
542 }
543
544 static unsigned int scaler_id;
545
scaler_probe(struct platform_device * pdev)546 static int scaler_probe(struct platform_device *pdev)
547 {
548 struct device_node *np = pdev->dev.of_node;
549 struct scaler_dev *scaler = NULL;
550 int ret = 0;
551
552 if (np == NULL) {
553 vin_err("Scaler failed to get of node\n");
554 return -ENODEV;
555 }
556
557 scaler = kzalloc(sizeof(struct scaler_dev), GFP_KERNEL);
558 if (!scaler) {
559 ret = -ENOMEM;
560 vin_err("sunxi scaler kzalloc failed!\n");
561 goto ekzalloc;
562 }
563 of_property_read_u32(np, "device_id", &pdev->id);
564 if (pdev->id < 0) {
565 vin_err("Scaler failed to get device id\n");
566 ret = -EINVAL;
567 goto freedev;
568 }
569
570 scaler->id = pdev->id;
571 scaler->pdev = pdev;
572
573 if (scaler->id > 0xf0) {
574 scaler->is_empty = 1;
575 scaler->id = scaler_id;
576 scaler_id++;
577 scaler->base = kzalloc(0x400, GFP_KERNEL);
578 } else {
579 scaler->base = of_iomap(np, 0);
580 }
581
582 if (!scaler->base) {
583 ret = -EIO;
584 goto freedev;
585 }
586 __scaler_init_subdev(scaler);
587
588 if (scaler_resource_alloc(scaler) < 0) {
589 ret = -ENOMEM;
590 goto unmap;
591 }
592
593 vipp_set_base_addr(scaler->id, (unsigned long)scaler->base);
594 vipp_map_reg_load_addr(scaler->id, (unsigned long)scaler->vipp_reg.vir_addr);
595 vipp_map_osd_para_load_addr(scaler->id, (unsigned long)scaler->osd_para.vir_addr);
596
597 platform_set_drvdata(pdev, scaler);
598
599 glb_vipp[scaler->id] = scaler;
600
601 vin_log(VIN_LOG_SCALER, "scaler%d probe end\n", scaler->id);
602 return 0;
603 unmap:
604 if (!scaler->is_empty)
605 iounmap(scaler->base);
606 else
607 kfree(scaler->base);
608 freedev:
609 kfree(scaler);
610 ekzalloc:
611 vin_err("scaler%d probe err!\n", scaler->id);
612 return ret;
613 }
614
scaler_remove(struct platform_device * pdev)615 static int scaler_remove(struct platform_device *pdev)
616 {
617 struct scaler_dev *scaler = platform_get_drvdata(pdev);
618 struct v4l2_subdev *sd = &scaler->subdev;
619
620 scaler_resource_free(scaler);
621 platform_set_drvdata(pdev, NULL);
622 v4l2_device_unregister_subdev(sd);
623 v4l2_set_subdevdata(sd, NULL);
624 if (scaler->base) {
625 if (!scaler->is_empty)
626 iounmap(scaler->base);
627 else
628 kfree(scaler->base);
629 }
630 kfree(scaler);
631 return 0;
632 }
633
634 static const struct of_device_id sunxi_scaler_match[] = {
635 {.compatible = "allwinner,sunxi-scaler",},
636 {},
637 };
638
639 static struct platform_driver scaler_platform_driver = {
640 .probe = scaler_probe,
641 .remove = scaler_remove,
642 .driver = {
643 .name = SCALER_MODULE_NAME,
644 .owner = THIS_MODULE,
645 .of_match_table = sunxi_scaler_match,
646 },
647 };
648
sunxi_scaler_get_subdev(int id)649 struct v4l2_subdev *sunxi_scaler_get_subdev(int id)
650 {
651 if (id < VIN_MAX_SCALER && glb_vipp[id])
652 return &glb_vipp[id]->subdev;
653 else
654 return NULL;
655 }
656 #if defined (CONFIG_ARCH_SUN8IW12P1)
sunxi_vipp_get_osd_stat(int id,unsigned int * stat)657 int sunxi_vipp_get_osd_stat(int id, unsigned int *stat)
658 {
659 struct v4l2_subdev *sd = sunxi_scaler_get_subdev(id);
660 struct scaler_dev *vipp = v4l2_get_subdevdata(sd);
661 unsigned long long *stat_buf = vipp->osd_stat.vir_addr;
662 int i;
663
664 if (stat_buf == NULL || stat == NULL)
665 return -EINVAL;
666
667 for (i = 0; i < MAX_OVERLAY_NUM; i++)
668 stat[i] = stat_buf[i];
669
670 return 0;
671 }
672 #endif
sunxi_scaler_platform_register(void)673 int sunxi_scaler_platform_register(void)
674 {
675 return platform_driver_register(&scaler_platform_driver);
676 }
677
sunxi_scaler_platform_unregister(void)678 void sunxi_scaler_platform_unregister(void)
679 {
680 platform_driver_unregister(&scaler_platform_driver);
681 vin_log(VIN_LOG_SCALER, "scaler_exit end\n");
682 }
683