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 272