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