252 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for Renesas RZ/G2L CRU4 *5 * Copyright (C) 2022 Renesas Electronics Corp.6 */7 8#include <linux/delay.h>9#include "rzg2l-cru.h"10 11struct rzg2l_cru_ip_format {12 u32 code;13 unsigned int datatype;14 unsigned int bpp;15};16 17static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {18 { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },19};20 21enum rzg2l_csi2_pads {22 RZG2L_CRU_IP_SINK = 0,23 RZG2L_CRU_IP_SOURCE,24};25 26static 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 37struct 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 49static 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 96static 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 else114 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 134static 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 145static 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 163static 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 180static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = {181 .s_stream = rzg2l_cru_ip_s_stream,182};183 184static 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 191static 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 196static const struct v4l2_subdev_internal_ops rzg2l_cru_ip_internal_ops = {197 .init_state = rzg2l_cru_ip_init_state,198};199 200static const struct media_entity_operations rzg2l_cru_ip_entity_ops = {201 .link_validate = v4l2_subdev_link_validate,202};203 204int 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;236error_subdev:237 v4l2_subdev_cleanup(&ip->subdev);238entity_cleanup:239 media_entity_cleanup(&ip->subdev.entity);240 241 return ret;242}243 244void 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