xref: /linux/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for STM32 Digital Camera Memory Interface Pixel Processor
4  *
5  * Copyright (C) STMicroelectronics SA 2023
6  * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
7  *          Alain Volmat <alain.volmat@foss.st.com>
8  *          for STMicroelectronics.
9  */
10 
11 #include <linux/vmalloc.h>
12 #include <linux/v4l2-mediabus.h>
13 #include <media/v4l2-rect.h>
14 #include <media/v4l2-subdev.h>
15 
16 #include "dcmipp-common.h"
17 
18 #define DCMIPP_P0FCTCR	0x500
19 #define DCMIPP_P0FCTCR_FRATE_MASK	GENMASK(1, 0)
20 #define DCMIPP_P0SCSTR	0x504
21 #define DCMIPP_P0SCSTR_HSTART_SHIFT	0
22 #define DCMIPP_P0SCSTR_VSTART_SHIFT	16
23 #define DCMIPP_P0SCSZR	0x508
24 #define DCMIPP_P0SCSZR_ENABLE		BIT(31)
25 #define DCMIPP_P0SCSZR_HSIZE_SHIFT	0
26 #define DCMIPP_P0SCSZR_VSIZE_SHIFT	16
27 #define DCMIPP_P0PPCR	0x5c0
28 #define DCMIPP_P0PPCR_BSM_1_2		0x1
29 #define DCMIPP_P0PPCR_BSM_1_4		0x2
30 #define DCMIPP_P0PPCR_BSM_2_4		0x3
31 #define DCMIPP_P0PPCR_BSM_MASK		GENMASK(8, 7)
32 #define DCMIPP_P0PPCR_BSM_SHIFT		0x7
33 #define DCMIPP_P0PPCR_LSM		BIT(10)
34 #define DCMIPP_P0PPCR_OELS		BIT(11)
35 
36 #define IS_SINK(pad) (!(pad))
37 #define IS_SRC(pad)  ((pad))
38 
39 struct dcmipp_byteproc_pix_map {
40 	unsigned int code;
41 	unsigned int bpp;
42 };
43 
44 #define PIXMAP_MBUS_BPP(mbus, byteperpixel)		\
45 	{						\
46 		.code = MEDIA_BUS_FMT_##mbus,		\
47 		.bpp = byteperpixel,			\
48 	}
49 static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {
50 	PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2),
51 	PIXMAP_MBUS_BPP(YUYV8_2X8, 2),
52 	PIXMAP_MBUS_BPP(YVYU8_2X8, 2),
53 	PIXMAP_MBUS_BPP(UYVY8_2X8, 2),
54 	PIXMAP_MBUS_BPP(VYUY8_2X8, 2),
55 	PIXMAP_MBUS_BPP(Y8_1X8, 1),
56 	PIXMAP_MBUS_BPP(SBGGR8_1X8, 1),
57 	PIXMAP_MBUS_BPP(SGBRG8_1X8, 1),
58 	PIXMAP_MBUS_BPP(SGRBG8_1X8, 1),
59 	PIXMAP_MBUS_BPP(SRGGB8_1X8, 1),
60 	PIXMAP_MBUS_BPP(JPEG_1X8, 1),
61 };
62 
63 static const struct dcmipp_byteproc_pix_map *
dcmipp_byteproc_pix_map_by_code(u32 code)64 dcmipp_byteproc_pix_map_by_code(u32 code)
65 {
66 	unsigned int i;
67 
68 	for (i = 0; i < ARRAY_SIZE(dcmipp_byteproc_pix_map_list); i++) {
69 		if (dcmipp_byteproc_pix_map_list[i].code == code)
70 			return &dcmipp_byteproc_pix_map_list[i];
71 	}
72 
73 	return NULL;
74 }
75 
76 struct dcmipp_byteproc_device {
77 	struct dcmipp_ent_device ved;
78 	struct v4l2_subdev sd;
79 	struct device *dev;
80 	void __iomem *regs;
81 	bool streaming;
82 };
83 
84 static const struct v4l2_mbus_framefmt fmt_default = {
85 	.width = DCMIPP_FMT_WIDTH_DEFAULT,
86 	.height = DCMIPP_FMT_HEIGHT_DEFAULT,
87 	.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
88 	.field = V4L2_FIELD_NONE,
89 	.colorspace = DCMIPP_COLORSPACE_DEFAULT,
90 	.ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
91 	.quantization = DCMIPP_QUANTIZATION_DEFAULT,
92 	.xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
93 };
94 
95 static const struct v4l2_rect crop_min = {
96 	.width = DCMIPP_FRAME_MIN_WIDTH,
97 	.height = DCMIPP_FRAME_MIN_HEIGHT,
98 	.top = 0,
99 	.left = 0,
100 };
101 
dcmipp_byteproc_adjust_crop(struct v4l2_rect * r,struct v4l2_rect * compose)102 static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r,
103 					struct v4l2_rect *compose)
104 {
105 	/* Disallow rectangles smaller than the minimal one. */
106 	v4l2_rect_set_min_size(r, &crop_min);
107 	v4l2_rect_map_inside(r, compose);
108 }
109 
dcmipp_byteproc_adjust_compose(struct v4l2_rect * r,const struct v4l2_mbus_framefmt * fmt)110 static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r,
111 					   const struct v4l2_mbus_framefmt *fmt)
112 {
113 	r->top = 0;
114 	r->left = 0;
115 
116 	/* Compose is not possible for JPEG or Bayer formats */
117 	if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 ||
118 	    fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
119 	    fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
120 	    fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
121 	    fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) {
122 		r->width = fmt->width;
123 		r->height = fmt->height;
124 		return;
125 	}
126 
127 	/* Adjust height - we can only perform 1/2 decimation */
128 	if (r->height <= (fmt->height / 2))
129 		r->height = fmt->height / 2;
130 	else
131 		r->height = fmt->height;
132 
133 	/* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */
134 	if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4))
135 		r->width = fmt->width / 4;
136 	else if (r->width <= (fmt->width / 2))
137 		r->width = fmt->width / 2;
138 	else
139 		r->width = fmt->width;
140 }
141 
dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt * fmt)142 static void dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
143 {
144 	const struct dcmipp_byteproc_pix_map *vpix;
145 
146 	/* Only accept code in the pix map table */
147 	vpix = dcmipp_byteproc_pix_map_by_code(fmt->code);
148 	if (!vpix)
149 		fmt->code = fmt_default.code;
150 
151 	fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
152 			     DCMIPP_FRAME_MAX_WIDTH) & ~1;
153 	fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
154 			      DCMIPP_FRAME_MAX_HEIGHT) & ~1;
155 
156 	if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
157 		fmt->field = fmt_default.field;
158 
159 	dcmipp_colorimetry_clamp(fmt);
160 }
161 
dcmipp_byteproc_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)162 static int dcmipp_byteproc_init_state(struct v4l2_subdev *sd,
163 				      struct v4l2_subdev_state *sd_state)
164 {
165 	unsigned int i;
166 
167 	for (i = 0; i < sd->entity.num_pads; i++) {
168 		struct v4l2_mbus_framefmt *mf;
169 		struct v4l2_rect *r;
170 
171 		mf = v4l2_subdev_state_get_format(sd_state, i);
172 		*mf = fmt_default;
173 
174 		if (IS_SINK(i))
175 			r = v4l2_subdev_state_get_compose(sd_state, i);
176 		else
177 			r = v4l2_subdev_state_get_crop(sd_state, i);
178 
179 		r->top = 0;
180 		r->left = 0;
181 		r->width = DCMIPP_FMT_WIDTH_DEFAULT;
182 		r->height = DCMIPP_FMT_HEIGHT_DEFAULT;
183 	}
184 
185 	return 0;
186 }
187 
188 static int
dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)189 dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev *sd,
190 			       struct v4l2_subdev_state *sd_state,
191 			       struct v4l2_subdev_mbus_code_enum *code)
192 {
193 	const struct dcmipp_byteproc_pix_map *vpix;
194 	struct v4l2_mbus_framefmt *sink_fmt;
195 
196 	if (IS_SINK(code->pad)) {
197 		if (code->index >= ARRAY_SIZE(dcmipp_byteproc_pix_map_list))
198 			return -EINVAL;
199 		vpix = &dcmipp_byteproc_pix_map_list[code->index];
200 		code->code = vpix->code;
201 	} else {
202 		/* byteproc doesn't support transformation on format */
203 		if (code->index > 0)
204 			return -EINVAL;
205 
206 		sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);
207 		code->code = sink_fmt->code;
208 	}
209 
210 	return 0;
211 }
212 
213 static int
dcmipp_byteproc_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)214 dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd,
215 				struct v4l2_subdev_state *sd_state,
216 				struct v4l2_subdev_frame_size_enum *fse)
217 {
218 	struct v4l2_rect *compose;
219 
220 	if (fse->index)
221 		return -EINVAL;
222 
223 	fse->min_width = DCMIPP_FRAME_MIN_WIDTH;
224 	fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;
225 
226 	if (IS_SINK(fse->pad)) {
227 		fse->max_width = DCMIPP_FRAME_MAX_WIDTH;
228 		fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;
229 	} else {
230 		compose = v4l2_subdev_state_get_compose(sd_state, 0);
231 		fse->max_width = compose->width;
232 		fse->max_height = compose->height;
233 	}
234 
235 	return 0;
236 }
237 
dcmipp_byteproc_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)238 static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,
239 				   struct v4l2_subdev_state *sd_state,
240 				   struct v4l2_subdev_format *fmt)
241 {
242 	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
243 	struct v4l2_mbus_framefmt *mf;
244 	struct v4l2_rect *crop, *compose;
245 
246 	if (byteproc->streaming)
247 		return -EBUSY;
248 
249 	mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
250 
251 	crop = v4l2_subdev_state_get_crop(sd_state, 1);
252 	compose = v4l2_subdev_state_get_compose(sd_state, 0);
253 
254 	if (IS_SRC(fmt->pad)) {
255 		fmt->format = *v4l2_subdev_state_get_format(sd_state, 0);
256 		fmt->format.width = crop->width;
257 		fmt->format.height = crop->height;
258 	} else {
259 		dcmipp_byteproc_adjust_fmt(&fmt->format);
260 		crop->top = 0;
261 		crop->left = 0;
262 		crop->width = fmt->format.width;
263 		crop->height = fmt->format.height;
264 		*compose = *crop;
265 		/* Set the same format on SOURCE pad as well */
266 		*v4l2_subdev_state_get_format(sd_state, 1) = fmt->format;
267 	}
268 	*mf = fmt->format;
269 
270 	return 0;
271 }
272 
dcmipp_byteproc_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * s)273 static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd,
274 					 struct v4l2_subdev_state *sd_state,
275 					 struct v4l2_subdev_selection *s)
276 {
277 	struct v4l2_mbus_framefmt *sink_fmt;
278 	struct v4l2_rect *crop, *compose;
279 
280 	/*
281 	 * In the HW, the decimation block is located prior to the crop hence:
282 	 * Compose is done on the sink pad
283 	 * Crop is done on the src pad
284 	 */
285 	if (IS_SINK(s->pad) &&
286 	    (s->target == V4L2_SEL_TGT_CROP ||
287 	     s->target == V4L2_SEL_TGT_CROP_BOUNDS ||
288 	     s->target == V4L2_SEL_TGT_CROP_DEFAULT))
289 		return -EINVAL;
290 
291 	if (IS_SRC(s->pad) &&
292 	    (s->target == V4L2_SEL_TGT_COMPOSE ||
293 	     s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||
294 	     s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT))
295 		return -EINVAL;
296 
297 	sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);
298 	crop = v4l2_subdev_state_get_crop(sd_state, 1);
299 	compose = v4l2_subdev_state_get_compose(sd_state, 0);
300 
301 	switch (s->target) {
302 	case V4L2_SEL_TGT_CROP:
303 		s->r = *crop;
304 		break;
305 	case V4L2_SEL_TGT_CROP_BOUNDS:
306 	case V4L2_SEL_TGT_CROP_DEFAULT:
307 		s->r = *compose;
308 		break;
309 	case V4L2_SEL_TGT_COMPOSE:
310 		s->r = *compose;
311 		break;
312 	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
313 	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
314 		s->r.top = 0;
315 		s->r.left = 0;
316 		s->r.width = sink_fmt->width;
317 		s->r.height = sink_fmt->height;
318 		break;
319 	default:
320 		return -EINVAL;
321 	}
322 
323 	return 0;
324 }
325 
dcmipp_byteproc_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * s)326 static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
327 					 struct v4l2_subdev_state *sd_state,
328 					 struct v4l2_subdev_selection *s)
329 {
330 	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
331 	struct v4l2_mbus_framefmt *mf;
332 	struct v4l2_rect *crop, *compose;
333 
334 	/*
335 	 * In the HW, the decimation block is located prior to the crop hence:
336 	 * Compose is done on the sink pad
337 	 * Crop is done on the src pad
338 	 */
339 	if ((s->target == V4L2_SEL_TGT_CROP ||
340 	     s->target == V4L2_SEL_TGT_CROP_BOUNDS ||
341 	     s->target == V4L2_SEL_TGT_CROP_DEFAULT) && IS_SINK(s->pad))
342 		return -EINVAL;
343 
344 	if ((s->target == V4L2_SEL_TGT_COMPOSE ||
345 	     s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||
346 	     s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT) && IS_SRC(s->pad))
347 		return -EINVAL;
348 
349 	crop = v4l2_subdev_state_get_crop(sd_state, 1);
350 	compose = v4l2_subdev_state_get_compose(sd_state, 0);
351 
352 	switch (s->target) {
353 	case V4L2_SEL_TGT_CROP:
354 		dcmipp_byteproc_adjust_crop(&s->r, compose);
355 
356 		*crop = s->r;
357 		mf = v4l2_subdev_state_get_format(sd_state, 1);
358 		mf->width = s->r.width;
359 		mf->height = s->r.height;
360 
361 		dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n",
362 			crop->width, crop->height, crop->left, crop->top);
363 		break;
364 	case V4L2_SEL_TGT_COMPOSE:
365 		mf = v4l2_subdev_state_get_format(sd_state, 0);
366 		dcmipp_byteproc_adjust_compose(&s->r, mf);
367 		*compose = s->r;
368 		*crop = s->r;
369 
370 		mf = v4l2_subdev_state_get_format(sd_state, 1);
371 		mf->width = s->r.width;
372 		mf->height = s->r.height;
373 
374 		dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n",
375 			compose->width, compose->height,
376 			compose->left, compose->top);
377 		break;
378 	default:
379 		return -EINVAL;
380 	}
381 
382 	return 0;
383 }
384 
385 static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = {
386 	.enum_mbus_code		= dcmipp_byteproc_enum_mbus_code,
387 	.enum_frame_size	= dcmipp_byteproc_enum_frame_size,
388 	.get_fmt		= v4l2_subdev_get_fmt,
389 	.set_fmt		= dcmipp_byteproc_set_fmt,
390 	.get_selection		= dcmipp_byteproc_get_selection,
391 	.set_selection		= dcmipp_byteproc_set_selection,
392 };
393 
dcmipp_byteproc_configure_scale_crop(struct dcmipp_byteproc_device * byteproc)394 static int dcmipp_byteproc_configure_scale_crop
395 			(struct dcmipp_byteproc_device *byteproc)
396 {
397 	const struct dcmipp_byteproc_pix_map *vpix;
398 	struct v4l2_subdev_state *state;
399 	struct v4l2_mbus_framefmt *sink_fmt;
400 	u32 hprediv, vprediv;
401 	struct v4l2_rect *compose, *crop;
402 	u32 val = 0;
403 
404 	state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd);
405 	sink_fmt = v4l2_subdev_state_get_format(state, 0);
406 	compose = v4l2_subdev_state_get_compose(state, 0);
407 	crop = v4l2_subdev_state_get_crop(state, 1);
408 	v4l2_subdev_unlock_state(state);
409 
410 	/* find output format bpp */
411 	vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code);
412 	if (!vpix)
413 		return -EINVAL;
414 
415 	/* clear decimation/crop */
416 	reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK);
417 	reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM);
418 	reg_write(byteproc, DCMIPP_P0SCSTR, 0);
419 	reg_write(byteproc, DCMIPP_P0SCSZR, 0);
420 
421 	/* Ignore decimation/crop with JPEG */
422 	if (vpix->code == MEDIA_BUS_FMT_JPEG_1X8)
423 		return 0;
424 
425 	/* decimation */
426 	hprediv = sink_fmt->width / compose->width;
427 	if (hprediv == 4)
428 		val |= DCMIPP_P0PPCR_BSM_1_4 << DCMIPP_P0PPCR_BSM_SHIFT;
429 	else if ((vpix->code == MEDIA_BUS_FMT_Y8_1X8) && (hprediv == 2))
430 		val |= DCMIPP_P0PPCR_BSM_1_2 << DCMIPP_P0PPCR_BSM_SHIFT;
431 	else if (hprediv == 2)
432 		val |= DCMIPP_P0PPCR_BSM_2_4 << DCMIPP_P0PPCR_BSM_SHIFT;
433 
434 	vprediv = sink_fmt->height / compose->height;
435 	if (vprediv == 2)
436 		val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;
437 
438 	/* decimate using bytes and lines skipping */
439 	if (val) {
440 		reg_set(byteproc, DCMIPP_P0PPCR, val);
441 
442 		dev_dbg(byteproc->dev, "decimate to %dx%d [prediv=%dx%d]\n",
443 			compose->width, compose->height,
444 			hprediv, vprediv);
445 	}
446 
447 	dev_dbg(byteproc->dev, "crop to %dx%d\n", crop->width, crop->height);
448 
449 	/* expressed in 32-bits words on X axis, lines on Y axis */
450 	reg_write(byteproc, DCMIPP_P0SCSTR,
451 		  (((crop->left * vpix->bpp) / 4) <<
452 		   DCMIPP_P0SCSTR_HSTART_SHIFT) |
453 		  (crop->top << DCMIPP_P0SCSTR_VSTART_SHIFT));
454 	reg_write(byteproc, DCMIPP_P0SCSZR,
455 		  DCMIPP_P0SCSZR_ENABLE |
456 		  (((crop->width * vpix->bpp) / 4) <<
457 		   DCMIPP_P0SCSZR_HSIZE_SHIFT) |
458 		  (crop->height << DCMIPP_P0SCSZR_VSIZE_SHIFT));
459 
460 	return 0;
461 }
462 
dcmipp_byteproc_s_stream(struct v4l2_subdev * sd,int enable)463 static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable)
464 {
465 	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
466 	struct v4l2_subdev *s_subdev;
467 	struct media_pad *pad;
468 	int ret = 0;
469 
470 	/* Get source subdev */
471 	pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
472 	if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
473 		return -EINVAL;
474 	s_subdev = media_entity_to_v4l2_subdev(pad->entity);
475 
476 	if (enable) {
477 		ret = dcmipp_byteproc_configure_scale_crop(byteproc);
478 		if (ret)
479 			return ret;
480 
481 		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
482 		if (ret < 0) {
483 			dev_err(byteproc->dev,
484 				"failed to start source subdev streaming (%d)\n",
485 				ret);
486 			return ret;
487 		}
488 	} else {
489 		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
490 		if (ret < 0) {
491 			dev_err(byteproc->dev,
492 				"failed to stop source subdev streaming (%d)\n",
493 				ret);
494 			return ret;
495 		}
496 	}
497 
498 	byteproc->streaming = enable;
499 
500 	return 0;
501 }
502 
503 static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = {
504 	.s_stream = dcmipp_byteproc_s_stream,
505 };
506 
507 static const struct v4l2_subdev_ops dcmipp_byteproc_ops = {
508 	.pad = &dcmipp_byteproc_pad_ops,
509 	.video = &dcmipp_byteproc_video_ops,
510 };
511 
dcmipp_byteproc_release(struct v4l2_subdev * sd)512 static void dcmipp_byteproc_release(struct v4l2_subdev *sd)
513 {
514 	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
515 
516 	kfree(byteproc);
517 }
518 
519 static const struct v4l2_subdev_internal_ops dcmipp_byteproc_int_ops = {
520 	.init_state = dcmipp_byteproc_init_state,
521 	.release = dcmipp_byteproc_release,
522 };
523 
dcmipp_byteproc_ent_release(struct dcmipp_ent_device * ved)524 void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved)
525 {
526 	struct dcmipp_byteproc_device *byteproc =
527 			container_of(ved, struct dcmipp_byteproc_device, ved);
528 
529 	dcmipp_ent_sd_unregister(ved, &byteproc->sd);
530 }
531 
532 struct dcmipp_ent_device *
dcmipp_byteproc_ent_init(struct device * dev,const char * entity_name,struct v4l2_device * v4l2_dev,void __iomem * regs)533 dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
534 			 struct v4l2_device *v4l2_dev, void __iomem *regs)
535 {
536 	struct dcmipp_byteproc_device *byteproc;
537 	const unsigned long pads_flag[] = {
538 		MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
539 	};
540 	int ret;
541 
542 	/* Allocate the byteproc struct */
543 	byteproc = kzalloc(sizeof(*byteproc), GFP_KERNEL);
544 	if (!byteproc)
545 		return ERR_PTR(-ENOMEM);
546 
547 	byteproc->regs = regs;
548 
549 	/* Initialize ved and sd */
550 	ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd,
551 				     v4l2_dev, entity_name,
552 				     MEDIA_ENT_F_PROC_VIDEO_SCALER,
553 				     ARRAY_SIZE(pads_flag), pads_flag,
554 				     &dcmipp_byteproc_int_ops,
555 				     &dcmipp_byteproc_ops,
556 				     NULL, NULL);
557 	if (ret) {
558 		kfree(byteproc);
559 		return ERR_PTR(ret);
560 	}
561 
562 	byteproc->dev = dev;
563 
564 	return &byteproc->ved;
565 }
566