• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &para);
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