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