brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · ac8ebae Raw
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