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 "rzg2l-cru.h" 9 10 struct rzg2l_cru_ip_format { 11 u32 code; 12 unsigned int datatype; 13 unsigned int bpp; 14 }; 15 16 static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { 17 { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, 18 }; 19 20 enum rzg2l_csi2_pads { 21 RZG2L_CRU_IP_SINK = 0, 22 RZG2L_CRU_IP_SOURCE, 23 }; 24 25 static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) 26 { 27 unsigned int i; 28 29 for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) 30 if (rzg2l_cru_ip_formats[i].code == code) 31 return &rzg2l_cru_ip_formats[i]; 32 33 return NULL; 34 } 35 36 struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) 37 { 38 struct v4l2_subdev_state *state; 39 struct v4l2_mbus_framefmt *fmt; 40 41 state = v4l2_subdev_lock_and_get_active_state(&cru->ip.subdev); 42 fmt = v4l2_subdev_get_pad_format(&cru->ip.subdev, state, 1); 43 v4l2_subdev_unlock_state(state); 44 45 return fmt; 46 } 47 48 static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable) 49 { 50 struct rzg2l_cru_dev *cru; 51 int s_stream_ret = 0; 52 int ret; 53 54 cru = v4l2_get_subdevdata(sd); 55 56 if (!enable) { 57 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 58 if (ret) 59 s_stream_ret = ret; 60 61 ret = v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 62 if (ret == -ENOIOCTLCMD) 63 ret = 0; 64 if (ret && !s_stream_ret) 65 s_stream_ret = ret; 66 rzg2l_cru_stop_image_processing(cru); 67 } else { 68 ret = v4l2_subdev_call(cru->ip.remote, video, pre_streamon, 0); 69 if (ret == -ENOIOCTLCMD) 70 ret = 0; 71 if (ret) 72 return ret; 73 74 ret = rzg2l_cru_start_image_processing(cru); 75 if (ret) { 76 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 77 return ret; 78 } 79 80 rzg2l_cru_vclk_unprepare(cru); 81 82 ret = v4l2_subdev_call(cru->ip.remote, video, s_stream, enable); 83 if (ret == -ENOIOCTLCMD) 84 ret = 0; 85 if (!ret) { 86 ret = rzg2l_cru_vclk_prepare(cru); 87 if (!ret) 88 return 0; 89 } else { 90 /* enable back vclk so that s_stream in error path disables it */ 91 if (rzg2l_cru_vclk_prepare(cru)) 92 dev_err(cru->dev, "Failed to enable vclk\n"); 93 } 94 95 s_stream_ret = ret; 96 97 v4l2_subdev_call(cru->ip.remote, video, post_streamoff); 98 rzg2l_cru_stop_image_processing(cru); 99 } 100 101 return s_stream_ret; 102 } 103 104 static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, 105 struct v4l2_subdev_state *state, 106 struct v4l2_subdev_format *fmt) 107 { 108 struct v4l2_mbus_framefmt *src_format; 109 struct v4l2_mbus_framefmt *sink_format; 110 111 src_format = v4l2_subdev_get_pad_format(sd, state, RZG2L_CRU_IP_SOURCE); 112 if (fmt->pad == RZG2L_CRU_IP_SOURCE) { 113 fmt->format = *src_format; 114 return 0; 115 } 116 117 sink_format = v4l2_subdev_get_pad_format(sd, state, fmt->pad); 118 119 if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) 120 sink_format->code = rzg2l_cru_ip_formats[0].code; 121 else 122 sink_format->code = fmt->format.code; 123 124 sink_format->field = V4L2_FIELD_NONE; 125 sink_format->colorspace = fmt->format.colorspace; 126 sink_format->xfer_func = fmt->format.xfer_func; 127 sink_format->ycbcr_enc = fmt->format.ycbcr_enc; 128 sink_format->quantization = fmt->format.quantization; 129 sink_format->width = clamp_t(u32, fmt->format.width, 130 RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH); 131 sink_format->height = clamp_t(u32, fmt->format.height, 132 RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT); 133 134 fmt->format = *sink_format; 135 136 /* propagate format to source pad */ 137 *src_format = *sink_format; 138 139 return 0; 140 } 141 142 static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd, 143 struct v4l2_subdev_state *state, 144 struct v4l2_subdev_mbus_code_enum *code) 145 { 146 if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) 147 return -EINVAL; 148 149 code->code = rzg2l_cru_ip_formats[code->index].code; 150 return 0; 151 } 152 153 static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, 154 struct v4l2_subdev_state *state, 155 struct v4l2_subdev_frame_size_enum *fse) 156 { 157 if (fse->index != 0) 158 return -EINVAL; 159 160 if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16) 161 return -EINVAL; 162 163 fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH; 164 fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; 165 fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH; 166 fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT; 167 168 return 0; 169 } 170 171 static int rzg2l_cru_ip_init_config(struct v4l2_subdev *sd, 172 struct v4l2_subdev_state *sd_state) 173 { 174 struct v4l2_subdev_format fmt = { .pad = RZG2L_CRU_IP_SINK, }; 175 176 fmt.format.width = RZG2L_CRU_MIN_INPUT_WIDTH; 177 fmt.format.height = RZG2L_CRU_MIN_INPUT_HEIGHT; 178 fmt.format.field = V4L2_FIELD_NONE; 179 fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16; 180 fmt.format.colorspace = V4L2_COLORSPACE_SRGB; 181 fmt.format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 182 fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT; 183 fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT; 184 185 return rzg2l_cru_ip_set_format(sd, sd_state, &fmt); 186 } 187 188 static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = { 189 .s_stream = rzg2l_cru_ip_s_stream, 190 }; 191 192 static const struct v4l2_subdev_pad_ops rzg2l_cru_ip_pad_ops = { 193 .enum_mbus_code = rzg2l_cru_ip_enum_mbus_code, 194 .enum_frame_size = rzg2l_cru_ip_enum_frame_size, 195 .init_cfg = rzg2l_cru_ip_init_config, 196 .get_fmt = v4l2_subdev_get_fmt, 197 .set_fmt = rzg2l_cru_ip_set_format, 198 }; 199 200 static const struct v4l2_subdev_ops rzg2l_cru_ip_subdev_ops = { 201 .video = &rzg2l_cru_ip_video_ops, 202 .pad = &rzg2l_cru_ip_pad_ops, 203 }; 204 205 static const struct media_entity_operations rzg2l_cru_ip_entity_ops = { 206 .link_validate = v4l2_subdev_link_validate, 207 }; 208 209 int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru) 210 { 211 struct rzg2l_cru_ip *ip = &cru->ip; 212 int ret; 213 214 ip->subdev.dev = cru->dev; 215 v4l2_subdev_init(&ip->subdev, &rzg2l_cru_ip_subdev_ops); 216 v4l2_set_subdevdata(&ip->subdev, cru); 217 snprintf(ip->subdev.name, sizeof(ip->subdev.name), 218 "cru-ip-%s", dev_name(cru->dev)); 219 ip->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 220 221 ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 222 ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops; 223 224 ip->pads[0].flags = MEDIA_PAD_FL_SINK; 225 ip->pads[1].flags = MEDIA_PAD_FL_SOURCE; 226 227 ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads); 228 if (ret) 229 return ret; 230 231 ret = v4l2_subdev_init_finalize(&ip->subdev); 232 if (ret < 0) 233 goto entity_cleanup; 234 235 ret = v4l2_device_register_subdev(&cru->v4l2_dev, &ip->subdev); 236 if (ret < 0) 237 goto error_subdev; 238 239 return 0; 240 error_subdev: 241 v4l2_subdev_cleanup(&ip->subdev); 242 entity_cleanup: 243 media_entity_cleanup(&ip->subdev.entity); 244 245 return ret; 246 } 247 248 void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru) 249 { 250 struct rzg2l_cru_ip *ip = &cru->ip; 251 252 media_entity_cleanup(&ip->subdev.entity); 253 v4l2_subdev_cleanup(&ip->subdev); 254 v4l2_device_unregister_subdev(&ip->subdev); 255 } 256