GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / media / platform / microchip / microchip-isc-scaler.c
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
19 static 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
28 static 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
48 static 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(&req_fmt->format.width, 16, UINT_MAX, 0,
65                               &req_fmt->format.height, 16, UINT_MAX, 0, 0);
66
67         isc_scaler_prepare_fmt(&req_fmt->format);
68
69         fmt = isc_find_format_by_code(isc, req_fmt->format.code, &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(&v4l2_try_fmt->width,
86                                       16, isc->max_width, 0,
87                                       &v4l2_try_fmt->height,
88                                       16, isc->max_height, 0, 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                 (&isc->scaler_format[ISC_SCALER_PAD_SOURCE].width, 16,
100                  isc->max_width, 0,
101                  &isc->scaler_format[ISC_SCALER_PAD_SOURCE].height, 16,
102                  isc->max_height, 0, 0);
103
104         return 0;
105 }
106
107 static 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
126 static 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
148 static 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
168 static 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
175 static const struct media_entity_operations isc_scaler_entity_ops = {
176         .link_validate = v4l2_subdev_link_validate,
177 };
178
179 static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = {
180         .pad = &isc_scaler_pad_ops,
181 };
182
183 static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = {
184         .init_state = isc_scaler_init_state,
185 };
186
187 int isc_scaler_init(struct isc_device *isc)
188 {
189         int ret;
190
191         v4l2_subdev_init(&isc->scaler_sd, &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(isc->scaler_sd.name, sizeof(isc->scaler_sd.name),
197                  "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(&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(&isc->scaler_sd.entity,
215                                      ISC_SCALER_PADS_NUM,
216                                      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(&isc->v4l2_dev, &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 }
230 EXPORT_SYMBOL_GPL(isc_scaler_init);
231
232 int isc_scaler_link(struct isc_device *isc)
233 {
234         int ret;
235
236         ret = media_create_pad_link(&isc->current_subdev->sd->entity,
237                                     isc->remote_pad, &isc->scaler_sd.entity,
238                                     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(&isc->scaler_sd.entity,
253                                     ISC_SCALER_PAD_SOURCE,
254                                     &isc->video_dev.entity, 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 }
270 EXPORT_SYMBOL_GPL(isc_scaler_link);
271