1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Microchip Image Sensor Controller (ISC) Scaler entity support
4 *
5 * Copyright (C) 2022 Microchip Technology, Inc.
6 *
7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
8 *
9 */
10
11#include <media/media-device.h>
12#include <media/media-entity.h>
13#include <media/v4l2-device.h>
14#include <media/v4l2-subdev.h>
15
16#include "microchip-isc-regs.h"
17#include "microchip-isc.h"
18
19static void isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt *framefmt)
20{
21 framefmt->colorspace = V4L2_COLORSPACE_SRGB;
22 framefmt->field = V4L2_FIELD_NONE;
23 framefmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
24 framefmt->quantization = V4L2_QUANTIZATION_DEFAULT;
25 framefmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
26};
27
28static int isc_scaler_get_fmt(struct v4l2_subdev *sd,
29 struct v4l2_subdev_state *sd_state,
30 struct v4l2_subdev_format *format)
31{
32 struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
33 struct v4l2_mbus_framefmt *v4l2_try_fmt;
34
35 if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
36 v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
37 format->pad);
38 format->format = *v4l2_try_fmt;
39
40 return 0;
41 }
42
43 format->format = isc->scaler_format[format->pad];
44
45 return 0;
46}
47
48static int isc_scaler_set_fmt(struct v4l2_subdev *sd,
49 struct v4l2_subdev_state *sd_state,
50 struct v4l2_subdev_format *req_fmt)
51{
52 struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
53 struct v4l2_mbus_framefmt *v4l2_try_fmt;
54 struct isc_format *fmt;
55 unsigned int i;
56
57 /* Source format is fixed, we cannot change it */
58 if (req_fmt->pad == ISC_SCALER_PAD_SOURCE) {
59 req_fmt->format = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
60 return 0;
61 }
62
63 /* There is no limit on the frame size on the sink pad */
64 v4l_bound_align_image(width: &req_fmt->format.width, wmin: 16, UINT_MAX, walign: 0,
65 height: &req_fmt->format.height, hmin: 16, UINT_MAX, halign: 0, salign: 0);
66
67 isc_scaler_prepare_fmt(framefmt: &req_fmt->format);
68
69 fmt = isc_find_format_by_code(isc, code: req_fmt->format.code, index: &i);
70
71 if (!fmt)
72 fmt = &isc->formats_list[0];
73
74 req_fmt->format.code = fmt->mbus_code;
75
76 if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
77 v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
78 req_fmt->pad);
79 *v4l2_try_fmt = req_fmt->format;
80 /* Trying on the sink pad makes the source pad change too */
81 v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,
82 ISC_SCALER_PAD_SOURCE);
83 *v4l2_try_fmt = req_fmt->format;
84
85 v4l_bound_align_image(width: &v4l2_try_fmt->width,
86 wmin: 16, wmax: isc->max_width, walign: 0,
87 height: &v4l2_try_fmt->height,
88 hmin: 16, hmax: isc->max_height, halign: 0, salign: 0);
89 /* if we are just trying, we are done */
90 return 0;
91 }
92
93 isc->scaler_format[ISC_SCALER_PAD_SINK] = req_fmt->format;
94
95 /* The source pad is the same as the sink, but we have to crop it */
96 isc->scaler_format[ISC_SCALER_PAD_SOURCE] =
97 isc->scaler_format[ISC_SCALER_PAD_SINK];
98 v4l_bound_align_image
99 (width: &isc->scaler_format[ISC_SCALER_PAD_SOURCE].width, wmin: 16,
100 wmax: isc->max_width, walign: 0,
101 height: &isc->scaler_format[ISC_SCALER_PAD_SOURCE].height, hmin: 16,
102 hmax: isc->max_height, halign: 0, salign: 0);
103
104 return 0;
105}
106
107static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd,
108 struct v4l2_subdev_state *sd_state,
109 struct v4l2_subdev_mbus_code_enum *code)
110{
111 struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
112
113 /*
114 * All formats supported by the ISC are supported by the scaler.
115 * Advertise the formats which the ISC can take as input, as the scaler
116 * entity cropping is part of the PFE module (parallel front end)
117 */
118 if (code->index < isc->formats_list_size) {
119 code->code = isc->formats_list[code->index].mbus_code;
120 return 0;
121 }
122
123 return -EINVAL;
124}
125
126static int isc_scaler_g_sel(struct v4l2_subdev *sd,
127 struct v4l2_subdev_state *sd_state,
128 struct v4l2_subdev_selection *sel)
129{
130 struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
131
132 if (sel->pad == ISC_SCALER_PAD_SOURCE)
133 return -EINVAL;
134
135 if (sel->target != V4L2_SEL_TGT_CROP_BOUNDS &&
136 sel->target != V4L2_SEL_TGT_CROP)
137 return -EINVAL;
138
139 sel->r.height = isc->scaler_format[ISC_SCALER_PAD_SOURCE].height;
140 sel->r.width = isc->scaler_format[ISC_SCALER_PAD_SOURCE].width;
141
142 sel->r.left = 0;
143 sel->r.top = 0;
144
145 return 0;
146}
147
148static int isc_scaler_init_state(struct v4l2_subdev *sd,
149 struct v4l2_subdev_state *sd_state)
150{
151 struct v4l2_mbus_framefmt *v4l2_try_fmt =
152 v4l2_subdev_state_get_format(sd_state, 0);
153 struct v4l2_rect *try_crop;
154 struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);
155
156 *v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE];
157
158 try_crop = v4l2_subdev_state_get_crop(sd_state, 0);
159
160 try_crop->top = 0;
161 try_crop->left = 0;
162 try_crop->width = v4l2_try_fmt->width;
163 try_crop->height = v4l2_try_fmt->height;
164
165 return 0;
166}
167
168static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = {
169 .enum_mbus_code = isc_scaler_enum_mbus_code,
170 .set_fmt = isc_scaler_set_fmt,
171 .get_fmt = isc_scaler_get_fmt,
172 .get_selection = isc_scaler_g_sel,
173};
174
175static const struct media_entity_operations isc_scaler_entity_ops = {
176 .link_validate = v4l2_subdev_link_validate,
177};
178
179static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = {
180 .pad = &isc_scaler_pad_ops,
181};
182
183static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = {
184 .init_state = isc_scaler_init_state,
185};
186
187int isc_scaler_init(struct isc_device *isc)
188{
189 int ret;
190
191 v4l2_subdev_init(sd: &isc->scaler_sd, ops: &xisc_scaler_subdev_ops);
192 isc->scaler_sd.internal_ops = &isc_scaler_internal_ops;
193
194 isc->scaler_sd.owner = THIS_MODULE;
195 isc->scaler_sd.dev = isc->dev;
196 snprintf(buf: isc->scaler_sd.name, size: sizeof(isc->scaler_sd.name),
197 fmt: "microchip_isc_scaler");
198
199 isc->scaler_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
200 isc->scaler_sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
201 isc->scaler_sd.entity.ops = &isc_scaler_entity_ops;
202 isc->scaler_pads[ISC_SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
203 isc->scaler_pads[ISC_SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
204
205 isc_scaler_prepare_fmt(framefmt: &isc->scaler_format[ISC_SCALER_PAD_SOURCE]);
206 isc->scaler_format[ISC_SCALER_PAD_SOURCE].height = isc->max_height;
207 isc->scaler_format[ISC_SCALER_PAD_SOURCE].width = isc->max_width;
208 isc->scaler_format[ISC_SCALER_PAD_SOURCE].code =
209 isc->formats_list[0].mbus_code;
210
211 isc->scaler_format[ISC_SCALER_PAD_SINK] =
212 isc->scaler_format[ISC_SCALER_PAD_SOURCE];
213
214 ret = media_entity_pads_init(entity: &isc->scaler_sd.entity,
215 num_pads: ISC_SCALER_PADS_NUM,
216 pads: isc->scaler_pads);
217 if (ret < 0) {
218 dev_err(isc->dev, "scaler sd media entity init failed\n");
219 return ret;
220 }
221
222 ret = v4l2_device_register_subdev(v4l2_dev: &isc->v4l2_dev, sd: &isc->scaler_sd);
223 if (ret < 0) {
224 dev_err(isc->dev, "scaler sd failed to register subdev\n");
225 return ret;
226 }
227
228 return ret;
229}
230EXPORT_SYMBOL_GPL(isc_scaler_init);
231
232int isc_scaler_link(struct isc_device *isc)
233{
234 int ret;
235
236 ret = media_create_pad_link(source: &isc->current_subdev->sd->entity,
237 source_pad: isc->remote_pad, sink: &isc->scaler_sd.entity,
238 sink_pad: ISC_SCALER_PAD_SINK,
239 MEDIA_LNK_FL_ENABLED |
240 MEDIA_LNK_FL_IMMUTABLE);
241
242 if (ret < 0) {
243 dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
244 isc->current_subdev->sd->entity.name,
245 isc->scaler_sd.entity.name);
246 return ret;
247 }
248
249 dev_dbg(isc->dev, "link with %s pad: %d\n",
250 isc->current_subdev->sd->name, isc->remote_pad);
251
252 ret = media_create_pad_link(source: &isc->scaler_sd.entity,
253 source_pad: ISC_SCALER_PAD_SOURCE,
254 sink: &isc->video_dev.entity, sink_pad: ISC_PAD_SINK,
255 MEDIA_LNK_FL_ENABLED |
256 MEDIA_LNK_FL_IMMUTABLE);
257
258 if (ret < 0) {
259 dev_err(isc->dev, "Failed to create pad link: %s to %s\n",
260 isc->scaler_sd.entity.name,
261 isc->video_dev.entity.name);
262 return ret;
263 }
264
265 dev_dbg(isc->dev, "link with %s pad: %d\n", isc->scaler_sd.name,
266 ISC_SCALER_PAD_SOURCE);
267
268 return ret;
269}
270EXPORT_SYMBOL_GPL(isc_scaler_link);
271
272

source code of linux/drivers/media/platform/microchip/microchip-isc-scaler.c