1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for Renesas RZ/G2L CRU 4 * 5 * Copyright (C) 2022 Renesas Electronics Corp. 6 */ 7 8 #include <linux/delay.h> 9 #include <media/mipi-csi2.h> 10 11 #include "rzg2l-cru.h" 12 #include "rzg2l-cru-regs.h" 13 14 static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { 15 { 16 .code = MEDIA_BUS_FMT_UYVY8_1X16, 17 .datatype = MIPI_CSI2_DT_YUV422_8B, 18 .format = V4L2_PIX_FMT_UYVY, 19 .bpp = 2, 20 .icndmr = ICnDMR_YCMODE_UYVY, 21 .yuv = true, 22 }, 23 { 24 .code = MEDIA_BUS_FMT_SBGGR8_1X8, 25 .format = V4L2_PIX_FMT_SBGGR8, 26 .datatype = MIPI_CSI2_DT_RAW8, 27 .bpp = 1, 28 .icndmr = 0, 29 .yuv = false, 30 }, 31 { 32 .code = MEDIA_BUS_FMT_SGBRG8_1X8, 33 .format = V4L2_PIX_FMT_SGBRG8, 34 .datatype = MIPI_CSI2_DT_RAW8, 35 .bpp = 1, 36 .icndmr = 0, 37 .yuv = false, 38 }, 39 { 40 .code = MEDIA_BUS_FMT_SGRBG8_1X8, 41 .format = V4L2_PIX_FMT_SGRBG8, 42 .datatype = MIPI_CSI2_DT_RAW8, 43 .bpp = 1, 44 .icndmr = 0, 45 .yuv = false, 46 }, 47 { 48 .code = MEDIA_BUS_FMT_SRGGB8_1X8, 49 .format = V4L2_PIX_FMT_SRGGB8, 50 .datatype = MIPI_CSI2_DT_RAW8, 51 .bpp = 1, 52 .icndmr = 0, 53 .yuv = false, 54 }, 55 }; 56 57 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) 58 { 59 unsigned int i; 60 61 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) 62 if (rzg2l_cru_ip_formats[i].code == code) 63 return &rzg2l_cru_ip_formats[i]; 64 65 return NULL; 66 } 67 68 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format) 69 { 70 unsigned int i; 71 72 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { 73 if (rzg2l_cru_ip_formats[i].format == format) 74 return &rzg2l_cru_ip_formats[i]; 75 } 76 77 return NULL; 78 } 79 80 const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) 81 { 82 if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 83 return NULL; 84 85 return &rzg2l_cru_ip_formats[index]; 86 } 87 88 struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) 89 { 90 struct v4l2_subdev_state *state; 91 struct v4l2_mbus_framefmt *fmt; 92 93 state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); 94 fmt = v4l2_subdev_state_get_format(state, 1); 95 v4l2_subdev_unlock_state(state); 96 97 return fmt; 98 } 99 100 static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable) 101 { 102 struct rzg2l_cru_dev *cru; 103 int s_stream_ret = 0; 104 int ret; 105 106 cru = v4l2_get_subdevdata(sd); 107 108 if (!enable) { 109 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 110 if (ret) 111 s_stream_ret = ret; 112 113 ret = v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 114 if (ret == -ENOIOCTLCMD) 115 ret = 0; 116 if (ret && !s_stream_ret) 117 s_stream_ret = ret; 118 rzg2l_cru_stop_image_processing(cru); 119 } else { 120 ret = v4l2_subdev_call(cru->ip.remote, video, pre_streamon, 0); 121 if (ret == -ENOIOCTLCMD) 122 ret = 0; 123 if (ret) 124 return ret; 125 126 fsleep(1000); 127 128 ret = rzg2l_cru_start_image_processing(cru); 129 if (ret) { 130 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 131 return ret; 132 } 133 134 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 135 if (!ret || ret == -ENOIOCTLCMD) 136 return 0; 137 138 s_stream_ret = ret; 139 140 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 141 rzg2l_cru_stop_image_processing(cru); 142 } 143 144 return s_stream_ret; 145 } 146 147 static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, 148 struct v4l2_subdev_state *state, 149 struct v4l2_subdev_format *fmt) 150 { 151 struct v4l2_mbus_framefmt *src_format; 152 struct v4l2_mbus_framefmt *sink_format; 153 154 src_format = v4l2_subdev_state_get_format(state, RZG2L_CRU_IP_SOURCE); 155 if (fmt->pad == RZG2L_CRU_IP_SOURCE) { 156 fmt->format = *src_format; 157 return 0; 158 } 159 160 sink_format = v4l2_subdev_state_get_format(state, fmt->pad); 161 162 if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) 163 sink_format->code = rzg2l_cru_ip_formats[0].code; 164 else 165 sink_format->code = fmt->format.code; 166 167 sink_format->field = V4L2_FIELD_NONE; 168 sink_format->colorspace = fmt->format.colorspace; 169 sink_format->xfer_func = fmt->format.xfer_func; 170 sink_format->ycbcr_enc = fmt->format.ycbcr_enc; 171 sink_format->quantization = fmt->format.quantization; 172 sink_format->width = clamp_t(u32, fmt->format.width, 173 RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH); 174 sink_format->height = clamp_t(u32, fmt->format.height, 175 RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT); 176 177 fmt->format = *sink_format; 178 179 /* propagate format to source pad */ 180 *src_format = *sink_format; 181 182 return 0; 183 } 184 185 static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd, 186 struct v4l2_subdev_state *state, 187 struct v4l2_subdev_mbus_code_enum *code) 188 { 189 if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 190 return -EINVAL; 191 192 code->code = rzg2l_cru_ip_formats[code->index].code; 193 return 0; 194 } 195 196 static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, 197 struct v4l2_subdev_state *state, 198 struct v4l2_subdev_frame_size_enum *fse) 199 { 200 if (fse->index != 0) 201 return -EINVAL; 202 203 if (!rzg2l_cru_ip_code_to_fmt(fse->code)) 204 return -EINVAL; 205 206 fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; 207 fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; 208 fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH; 209 fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT; 210 211 return 0; 212 } 213 214 static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd, 215 struct v4l2_subdev_state *sd_state) 216 { 217 struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; 218 219 fmt.format.width = RZG2L_CRU_MIN_INPUT_WIDTH; 220 fmt.format.height = RZG2L_CRU_MIN_INPUT_HEIGHT; 221 fmt.format.field = V4L2_FIELD_NONE; 222 fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; 223 fmt.format.colorspace = V4L2_COLORSPACE_SRGB; 224 fmt.format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 225 fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT; 226 fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT; 227 228 return rzg2l_cru_ip_set_format(sd, sd_state, &fmt); 229 } 230 231 static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { 232 .s_stream = rzg2l_cru_ip_s_stream, 233 }; 234 235 static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { 236 .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, 237 .enum_frame_size = rzg2l_cru_ip_enum_frame_size, 238 .get_fmt = v4l2_subdev_get_fmt, 239 .set_fmt = rzg2l_cru_ip_set_format, 240 }; 241 242 static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { 243 .video = &rzg2l_cru_ip_video_ops, 244 .pad = &rzg2l_cru_ip_pad_ops, 245 }; 246 247 static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = { 248 .init_state = rzg2l_cru_ip_init_state, 249 }; 250 251 static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { 252 .link_validate = v4l2_subdev_link_validate, 253 }; 254 255 int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) 256 { 257 struct rzg2l_cru_ip *ip = &cru->ip; 258 int ret; 259 260 ip->subdev.dev = cru->dev; 261 v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); 262 ip->subdev.internal_ops = &rzg2l_cru_ip_internal_ops; 263 v4l2_set_subdevdata(&ip->subdev, cru); 264 snprintf(ip->subdev.name, sizeof(ip->subdev.name), 265 "cru-ip-%s", dev_name(cru->dev)); 266 ip->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 267 268 ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 269 ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; 270 271 ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK | 272 MEDIA_PAD_FL_MUST_CONNECT; 273 ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE | 274 MEDIA_PAD_FL_MUST_CONNECT; 275 276 ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); 277 if (ret) 278 return ret; 279 280 ret = v4l2_subdev_init_finalize(&ip->subdev); 281 if (ret < 0) 282 goto entity_cleanup; 283 284 ret = v4l2_device_register_subdev(&cru->v4l2_dev, &ip->subdev); 285 if (ret < 0) 286 goto error_subdev; 287 288 return 0; 289 error_subdev: 290 v4l2_subdev_cleanup(&ip->subdev); 291 entity_cleanup: 292 media_entity_cleanup(&ip->subdev.entity); 293 294 return ret; 295 } 296 297 void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru) 298 { 299 struct rzg2l_cru_ip *ip = &cru->ip; 300 301 media_entity_cleanup(&ip->subdev.entity); 302 v4l2_subdev_cleanup(&ip->subdev); 303 v4l2_device_unregister_subdev(&ip->subdev); 304 } 305