1920b2665SEugen Hristev // SPDX-License-Identifier: GPL-2.0-only
2920b2665SEugen Hristev /*
3920b2665SEugen Hristev * Microchip Image Sensor Controller (ISC) Scaler entity support
4920b2665SEugen Hristev *
5920b2665SEugen Hristev * Copyright (C) 2022 Microchip Technology, Inc.
6920b2665SEugen Hristev *
7920b2665SEugen Hristev * Author: Eugen Hristev <eugen.hristev@microchip.com>
8920b2665SEugen Hristev *
9920b2665SEugen Hristev */
10920b2665SEugen Hristev
11920b2665SEugen Hristev #include <media/media-device.h>
12920b2665SEugen Hristev #include <media/media-entity.h>
13920b2665SEugen Hristev #include <media/v4l2-device.h>
14920b2665SEugen Hristev #include <media/v4l2-subdev.h>
15920b2665SEugen Hristev
16920b2665SEugen Hristev #include "microchip-isc-regs.h"
17920b2665SEugen Hristev #include "microchip-isc.h"
18920b2665SEugen Hristev
isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt * framefmt)19920b2665SEugen Hristev static void isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt *framefmt)
20920b2665SEugen Hristev {
21920b2665SEugen Hristev framefmt->colorspace = V4L2_COLORSPACE_SRGB;
22920b2665SEugen Hristev framefmt->field = V4L2_FIELD_NONE;
23920b2665SEugen Hristev framefmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
24920b2665SEugen Hristev framefmt->quantization = V4L2_QUANTIZATION_DEFAULT;
25920b2665SEugen Hristev framefmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
26920b2665SEugen Hristev };
27920b2665SEugen Hristev
isc_scaler_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)28920b2665SEugen Hristev static int isc_scaler_get_fmt(struct v4l2_subdev *sd,
29920b2665SEugen Hristev struct v4l2_subdev_state *sd_state,
30920b2665SEugen Hristev struct v4l2_subdev_format *format)
31920b2665SEugen Hristev {
32920b2665SEugen Hristev struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
33920b2665SEugen Hristev struct v4l2_mbus_framefmt *v4l2_try_fmt;
34920b2665SEugen Hristev
35920b2665SEugen Hristev if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
36920b2665SEugen Hristev v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state,
37920b2665SEugen Hristev format->pad);
38920b2665SEugen Hristev format->format = *v4l2_try_fmt;
39920b2665SEugen Hristev
40920b2665SEugen Hristev return 0;
41920b2665SEugen Hristev }
42920b2665SEugen Hristev
43920b2665SEugen Hristev format->format = isc->scaler_format[format->pad];
44920b2665SEugen Hristev
45920b2665SEugen Hristev return 0;
46920b2665SEugen Hristev }
47920b2665SEugen Hristev
isc_scaler_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * req_fmt)48920b2665SEugen Hristev static int isc_scaler_set_fmt(struct v4l2_subdev *sd,
49920b2665SEugen Hristev struct v4l2_subdev_state *sd_state,
50920b2665SEugen Hristev struct v4l2_subdev_format *req_fmt)
51920b2665SEugen Hristev {
52920b2665SEugen Hristev struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
53920b2665SEugen Hristev struct v4l2_mbus_framefmt *v4l2_try_fmt;
54920b2665SEugen Hristev struct isc_format *fmt;
55920b2665SEugen Hristev unsigned int i;
56920b2665SEugen Hristev
57920b2665SEugen Hristev /* Source format is fixed, we cannot change it */
58920b2665SEugen Hristev if (req_fmt->pad == ISC_SCALER_PAD_SOURCE) {
59920b2665SEugen Hristev req_fmt->format = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
60920b2665SEugen Hristev return 0;
61920b2665SEugen Hristev }
62920b2665SEugen Hristev
63920b2665SEugen Hristev /* There is no limit on the frame size on the sink pad */
64920b2665SEugen Hristev v4l_bound_align_image(&req_fmt->format.width, 16, UINT_MAX, 0,
65920b2665SEugen Hristev &req_fmt->format.height, 16, UINT_MAX, 0, 0);
66920b2665SEugen Hristev
67920b2665SEugen Hristev isc_scaler_prepare_fmt(&req_fmt->format);
68920b2665SEugen Hristev
69920b2665SEugen Hristev fmt = isc_find_format_by_code(isc, req_fmt->format.code, &i);
70920b2665SEugen Hristev
71920b2665SEugen Hristev if (!fmt)
72920b2665SEugen Hristev fmt = &isc->formats_list[0];
73920b2665SEugen Hristev
74920b2665SEugen Hristev req_fmt->format.code = fmt->mbus_code;
75920b2665SEugen Hristev
76920b2665SEugen Hristev if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
77920b2665SEugen Hristev v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state,
78920b2665SEugen Hristev req_fmt->pad);
79920b2665SEugen Hristev *v4l2_try_fmt = req_fmt->format;
80920b2665SEugen Hristev /* Trying on the sink pad makes the source pad change too */
81920b2665SEugen Hristev v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state,
82920b2665SEugen Hristev ISC_SCALER_PAD_SOURCE);
83920b2665SEugen Hristev *v4l2_try_fmt = req_fmt->format;
84920b2665SEugen Hristev
85920b2665SEugen Hristev v4l_bound_align_image(&v4l2_try_fmt->width,
86920b2665SEugen Hristev 16, isc->max_width, 0,
87920b2665SEugen Hristev &v4l2_try_fmt->height,
88920b2665SEugen Hristev 16, isc->max_height, 0, 0);
89920b2665SEugen Hristev /* if we are just trying, we are done */
90920b2665SEugen Hristev return 0;
91920b2665SEugen Hristev }
92920b2665SEugen Hristev
93920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SINK] = req_fmt->format;
94920b2665SEugen Hristev
95920b2665SEugen Hristev /* The source pad is the same as the sink, but we have to crop it */
96920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SOURCE] =
97920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SINK];
98920b2665SEugen Hristev v4l_bound_align_image
99920b2665SEugen Hristev (&isc->scaler_format[ISC_SCALER_PAD_SOURCE].width, 16,
100920b2665SEugen Hristev isc->max_width, 0,
101920b2665SEugen Hristev &isc->scaler_format[ISC_SCALER_PAD_SOURCE].height, 16,
102920b2665SEugen Hristev isc->max_height, 0, 0);
103920b2665SEugen Hristev
104920b2665SEugen Hristev return 0;
105920b2665SEugen Hristev }
106920b2665SEugen Hristev
isc_scaler_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)107920b2665SEugen Hristev static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd,
108920b2665SEugen Hristev struct v4l2_subdev_state *sd_state,
109920b2665SEugen Hristev struct v4l2_subdev_mbus_code_enum *code)
110920b2665SEugen Hristev {
111920b2665SEugen Hristev struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
112920b2665SEugen Hristev
113920b2665SEugen Hristev /*
114920b2665SEugen Hristev * All formats supported by the ISC are supported by the scaler.
115920b2665SEugen Hristev * Advertise the formats which the ISC can take as input, as the scaler
116920b2665SEugen Hristev * entity cropping is part of the PFE module (parallel front end)
117920b2665SEugen Hristev */
118920b2665SEugen Hristev if (code->index < isc->formats_list_size) {
119920b2665SEugen Hristev code->code = isc->formats_list[code->index].mbus_code;
120920b2665SEugen Hristev return 0;
121920b2665SEugen Hristev }
122920b2665SEugen Hristev
123920b2665SEugen Hristev return -EINVAL;
124920b2665SEugen Hristev }
125920b2665SEugen Hristev
isc_scaler_g_sel(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)126920b2665SEugen Hristev static int isc_scaler_g_sel(struct v4l2_subdev *sd,
127920b2665SEugen Hristev struct v4l2_subdev_state *sd_state,
128920b2665SEugen Hristev struct v4l2_subdev_selection *sel)
129920b2665SEugen Hristev {
130920b2665SEugen Hristev struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
131920b2665SEugen Hristev
132920b2665SEugen Hristev if (sel->pad == ISC_SCALER_PAD_SOURCE)
133920b2665SEugen Hristev return -EINVAL;
134920b2665SEugen Hristev
135920b2665SEugen Hristev if (sel->target != V4L2_SEL_TGT_CROP_BOUNDS &&
136920b2665SEugen Hristev sel->target != V4L2_SEL_TGT_CROP)
137920b2665SEugen Hristev return -EINVAL;
138920b2665SEugen Hristev
139920b2665SEugen Hristev sel->r.height = isc->scaler_format[ISC_SCALER_PAD_SOURCE].height;
140920b2665SEugen Hristev sel->r.width = isc->scaler_format[ISC_SCALER_PAD_SOURCE].width;
141920b2665SEugen Hristev
142920b2665SEugen Hristev sel->r.left = 0;
143920b2665SEugen Hristev sel->r.top = 0;
144920b2665SEugen Hristev
145920b2665SEugen Hristev return 0;
146920b2665SEugen Hristev }
147920b2665SEugen Hristev
isc_scaler_init_cfg(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)148920b2665SEugen Hristev static int isc_scaler_init_cfg(struct v4l2_subdev *sd,
149920b2665SEugen Hristev struct v4l2_subdev_state *sd_state)
150920b2665SEugen Hristev {
151920b2665SEugen Hristev struct v4l2_mbus_framefmt *v4l2_try_fmt =
152920b2665SEugen Hristev v4l2_subdev_get_try_format(sd, sd_state, 0);
153920b2665SEugen Hristev struct v4l2_rect *try_crop;
154920b2665SEugen Hristev struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
155920b2665SEugen Hristev
156920b2665SEugen Hristev *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
157920b2665SEugen Hristev
158920b2665SEugen Hristev try_crop = v4l2_subdev_get_try_crop(sd, sd_state, 0);
159920b2665SEugen Hristev
160920b2665SEugen Hristev try_crop->top = 0;
161920b2665SEugen Hristev try_crop->left = 0;
162920b2665SEugen Hristev try_crop->width = v4l2_try_fmt->width;
163920b2665SEugen Hristev try_crop->height = v4l2_try_fmt->height;
164920b2665SEugen Hristev
165920b2665SEugen Hristev return 0;
166920b2665SEugen Hristev }
167920b2665SEugen Hristev
168920b2665SEugen Hristev static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = {
169920b2665SEugen Hristev .enum_mbus_code = isc_scaler_enum_mbus_code,
170920b2665SEugen Hristev .set_fmt = isc_scaler_set_fmt,
171920b2665SEugen Hristev .get_fmt = isc_scaler_get_fmt,
172920b2665SEugen Hristev .get_selection = isc_scaler_g_sel,
173920b2665SEugen Hristev .init_cfg = isc_scaler_init_cfg,
174920b2665SEugen Hristev };
175920b2665SEugen Hristev
176*78ba0d79SEugen Hristev static const struct media_entity_operations isc_scaler_entity_ops = {
177*78ba0d79SEugen Hristev .link_validate = v4l2_subdev_link_validate,
178*78ba0d79SEugen Hristev };
179*78ba0d79SEugen Hristev
180920b2665SEugen Hristev static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = {
181920b2665SEugen Hristev .pad = &isc_scaler_pad_ops,
182920b2665SEugen Hristev };
183920b2665SEugen Hristev
isc_scaler_init(struct isc_device * isc)184920b2665SEugen Hristev int isc_scaler_init(struct isc_device *isc)
185920b2665SEugen Hristev {
186920b2665SEugen Hristev int ret;
187920b2665SEugen Hristev
188920b2665SEugen Hristev v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops);
189920b2665SEugen Hristev
190920b2665SEugen Hristev isc->scaler_sd.owner = THIS_MODULE;
191920b2665SEugen Hristev isc->scaler_sd.dev = isc->dev;
192920b2665SEugen Hristev snprintf(isc->scaler_sd.name, sizeof(isc->scaler_sd.name),
193920b2665SEugen Hristev "microchip_isc_scaler");
194920b2665SEugen Hristev
195920b2665SEugen Hristev isc->scaler_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
196920b2665SEugen Hristev isc->scaler_sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
197*78ba0d79SEugen Hristev isc->scaler_sd.entity.ops = &isc_scaler_entity_ops;
198920b2665SEugen Hristev isc->scaler_pads[ISC_SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
199920b2665SEugen Hristev isc->scaler_pads[ISC_SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
200920b2665SEugen Hristev
201920b2665SEugen Hristev isc_scaler_prepare_fmt(&isc->scaler_format[ISC_SCALER_PAD_SOURCE]);
202920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SOURCE].height = isc->max_height;
203920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SOURCE].width = isc->max_width;
204920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SOURCE].code =
205920b2665SEugen Hristev isc->formats_list[0].mbus_code;
206920b2665SEugen Hristev
207920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SINK] =
208920b2665SEugen Hristev isc->scaler_format[ISC_SCALER_PAD_SOURCE];
209920b2665SEugen Hristev
210920b2665SEugen Hristev ret = media_entity_pads_init(&isc->scaler_sd.entity,
211920b2665SEugen Hristev ISC_SCALER_PADS_NUM,
212920b2665SEugen Hristev isc->scaler_pads);
213920b2665SEugen Hristev if (ret < 0) {
214920b2665SEugen Hristev dev_err(isc->dev, "scaler sd media entity init failed\n");
215920b2665SEugen Hristev return ret;
216920b2665SEugen Hristev }
217920b2665SEugen Hristev
218920b2665SEugen Hristev ret = v4l2_device_register_subdev(&isc->v4l2_dev, &isc->scaler_sd);
219920b2665SEugen Hristev if (ret < 0) {
220920b2665SEugen Hristev dev_err(isc->dev, "scaler sd failed to register subdev\n");
221920b2665SEugen Hristev return ret;
222920b2665SEugen Hristev }
223920b2665SEugen Hristev
224920b2665SEugen Hristev return ret;
225920b2665SEugen Hristev }
226920b2665SEugen Hristev EXPORT_SYMBOL_GPL(isc_scaler_init);
227920b2665SEugen Hristev
isc_scaler_link(struct isc_device * isc)228920b2665SEugen Hristev int isc_scaler_link(struct isc_device *isc)
229920b2665SEugen Hristev {
230920b2665SEugen Hristev int ret;
231920b2665SEugen Hristev
232920b2665SEugen Hristev ret = media_create_pad_link(&isc->current_subdev->sd->entity,
233920b2665SEugen Hristev isc->remote_pad, &isc->scaler_sd.entity,
234920b2665SEugen Hristev ISC_SCALER_PAD_SINK,
235920b2665SEugen Hristev MEDIA_LNK_FL_ENABLED |
236920b2665SEugen Hristev MEDIA_LNK_FL_IMMUTABLE);
237920b2665SEugen Hristev
238920b2665SEugen Hristev if (ret < 0) {
239920b2665SEugen Hristev dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
240920b2665SEugen Hristev isc->current_subdev->sd->entity.name,
241920b2665SEugen Hristev isc->scaler_sd.entity.name);
242920b2665SEugen Hristev return ret;
243920b2665SEugen Hristev }
244920b2665SEugen Hristev
245920b2665SEugen Hristev dev_dbg(isc->dev, "link with %s pad: %d\n",
246920b2665SEugen Hristev isc->current_subdev->sd->name, isc->remote_pad);
247920b2665SEugen Hristev
248920b2665SEugen Hristev ret = media_create_pad_link(&isc->scaler_sd.entity,
249920b2665SEugen Hristev ISC_SCALER_PAD_SOURCE,
250920b2665SEugen Hristev &isc->video_dev.entity, ISC_PAD_SINK,
251920b2665SEugen Hristev MEDIA_LNK_FL_ENABLED |
252920b2665SEugen Hristev MEDIA_LNK_FL_IMMUTABLE);
253920b2665SEugen Hristev
254920b2665SEugen Hristev if (ret < 0) {
255920b2665SEugen Hristev dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
256920b2665SEugen Hristev isc->scaler_sd.entity.name,
257920b2665SEugen Hristev isc->video_dev.entity.name);
258920b2665SEugen Hristev return ret;
259920b2665SEugen Hristev }
260920b2665SEugen Hristev
261920b2665SEugen Hristev dev_dbg(isc->dev, "link with %s pad: %d\n", isc->scaler_sd.name,
262920b2665SEugen Hristev ISC_SCALER_PAD_SOURCE);
263920b2665SEugen Hristev
264920b2665SEugen Hristev return ret;
265920b2665SEugen Hristev }
266920b2665SEugen Hristev EXPORT_SYMBOL_GPL(isc_scaler_link);
267920b2665SEugen Hristev
268