brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · e834635 Raw
272 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Microchip Image Sensor Controller (ISC) Scaler entity support4 *5 * Copyright (C) 2022 Microchip Technology, Inc.6 *7 * Author: Eugen Hristev <eugen.hristev@microchip.com>8 *9 */10 11#include <media/media-device.h>12#include <media/media-entity.h>13#include <media/v4l2-device.h>14#include <media/v4l2-subdev.h>15 16#include "microchip-isc-regs.h"17#include "microchip-isc.h"18 19static void isc_scaler_prepare_fmt(struct v4l2_mbus_framefmt *framefmt)20{21	framefmt->colorspace = V4L2_COLORSPACE_SRGB;22	framefmt->field = V4L2_FIELD_NONE;23	framefmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;24	framefmt->quantization = V4L2_QUANTIZATION_DEFAULT;25	framefmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;26};27 28static int isc_scaler_get_fmt(struct v4l2_subdev *sd,29			      struct v4l2_subdev_state *sd_state,30			      struct v4l2_subdev_format *format)31{32	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);33	struct v4l2_mbus_framefmt *v4l2_try_fmt;34 35	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {36		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,37							    format->pad);38		format->format = *v4l2_try_fmt;39 40		return 0;41	}42 43	format->format = isc->scaler_format[format->pad];44 45	return 0;46}47 48static int isc_scaler_set_fmt(struct v4l2_subdev *sd,49			      struct v4l2_subdev_state *sd_state,50			      struct v4l2_subdev_format *req_fmt)51{52	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);53	struct v4l2_mbus_framefmt *v4l2_try_fmt;54	struct isc_format *fmt;55	unsigned int i;56 57	/* Source format is fixed, we cannot change it */58	if (req_fmt->pad == ISC_SCALER_PAD_SOURCE) {59		req_fmt->format = isc->scaler_format[ISC_SCALER_PAD_SOURCE];60		return 0;61	}62 63	/* There is no limit on the frame size on the sink pad */64	v4l_bound_align_image(&req_fmt->format.width, 16, UINT_MAX, 0,65			      &req_fmt->format.height, 16, UINT_MAX, 0, 0);66 67	isc_scaler_prepare_fmt(&req_fmt->format);68 69	fmt = isc_find_format_by_code(isc, req_fmt->format.code, &i);70 71	if (!fmt)72		fmt = &isc->formats_list[0];73 74	req_fmt->format.code = fmt->mbus_code;75 76	if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {77		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,78							    req_fmt->pad);79		*v4l2_try_fmt = req_fmt->format;80		/* Trying on the sink pad makes the source pad change too */81		v4l2_try_fmt = v4l2_subdev_state_get_format(sd_state,82							    ISC_SCALER_PAD_SOURCE);83		*v4l2_try_fmt = req_fmt->format;84 85		v4l_bound_align_image(&v4l2_try_fmt->width,86				      16, isc->max_width, 0,87				      &v4l2_try_fmt->height,88				      16, isc->max_height, 0, 0);89		/* if we are just trying, we are done */90		return 0;91	}92 93	isc->scaler_format[ISC_SCALER_PAD_SINK] = req_fmt->format;94 95	/* The source pad is the same as the sink, but we have to crop it */96	isc->scaler_format[ISC_SCALER_PAD_SOURCE] =97		isc->scaler_format[ISC_SCALER_PAD_SINK];98	v4l_bound_align_image99		(&isc->scaler_format[ISC_SCALER_PAD_SOURCE].width, 16,100		 isc->max_width, 0,101		 &isc->scaler_format[ISC_SCALER_PAD_SOURCE].height, 16,102		 isc->max_height, 0, 0);103 104	return 0;105}106 107static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd,108				     struct v4l2_subdev_state *sd_state,109				     struct v4l2_subdev_mbus_code_enum *code)110{111	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);112 113	/*114	 * All formats supported by the ISC are supported by the scaler.115	 * Advertise the formats which the ISC can take as input, as the scaler116	 * entity cropping is part of the PFE module (parallel front end)117	 */118	if (code->index < isc->formats_list_size) {119		code->code = isc->formats_list[code->index].mbus_code;120		return 0;121	}122 123	return -EINVAL;124}125 126static int isc_scaler_g_sel(struct v4l2_subdev *sd,127			    struct v4l2_subdev_state *sd_state,128			    struct v4l2_subdev_selection *sel)129{130	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);131 132	if (sel->pad == ISC_SCALER_PAD_SOURCE)133		return -EINVAL;134 135	if (sel->target != V4L2_SEL_TGT_CROP_BOUNDS &&136	    sel->target != V4L2_SEL_TGT_CROP)137		return -EINVAL;138 139	sel->r.height = isc->scaler_format[ISC_SCALER_PAD_SOURCE].height;140	sel->r.width = isc->scaler_format[ISC_SCALER_PAD_SOURCE].width;141 142	sel->r.left = 0;143	sel->r.top = 0;144 145	return 0;146}147 148static int isc_scaler_init_state(struct v4l2_subdev *sd,149				 struct v4l2_subdev_state *sd_state)150{151	struct v4l2_mbus_framefmt *v4l2_try_fmt =152		v4l2_subdev_state_get_format(sd_state, 0);153	struct v4l2_rect *try_crop;154	struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd);155 156	*v4l2_try_fmt = isc->scaler_format[ISC_SCALER_PAD_SOURCE];157 158	try_crop = v4l2_subdev_state_get_crop(sd_state, 0);159 160	try_crop->top = 0;161	try_crop->left = 0;162	try_crop->width = v4l2_try_fmt->width;163	try_crop->height = v4l2_try_fmt->height;164 165	return 0;166}167 168static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = {169	.enum_mbus_code = isc_scaler_enum_mbus_code,170	.set_fmt = isc_scaler_set_fmt,171	.get_fmt = isc_scaler_get_fmt,172	.get_selection = isc_scaler_g_sel,173};174 175static const struct media_entity_operations isc_scaler_entity_ops = {176	.link_validate = v4l2_subdev_link_validate,177};178 179static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = {180	.pad = &isc_scaler_pad_ops,181};182 183static const struct v4l2_subdev_internal_ops isc_scaler_internal_ops = {184	.init_state = isc_scaler_init_state,185};186 187int isc_scaler_init(struct isc_device *isc)188{189	int ret;190 191	v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops);192	isc->scaler_sd.internal_ops = &isc_scaler_internal_ops;193 194	isc->scaler_sd.owner = THIS_MODULE;195	isc->scaler_sd.dev = isc->dev;196	snprintf(isc->scaler_sd.name, sizeof(isc->scaler_sd.name),197		 "microchip_isc_scaler");198 199	isc->scaler_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;200	isc->scaler_sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;201	isc->scaler_sd.entity.ops = &isc_scaler_entity_ops;202	isc->scaler_pads[ISC_SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;203	isc->scaler_pads[ISC_SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;204 205	isc_scaler_prepare_fmt(&isc->scaler_format[ISC_SCALER_PAD_SOURCE]);206	isc->scaler_format[ISC_SCALER_PAD_SOURCE].height = isc->max_height;207	isc->scaler_format[ISC_SCALER_PAD_SOURCE].width = isc->max_width;208	isc->scaler_format[ISC_SCALER_PAD_SOURCE].code =209		 isc->formats_list[0].mbus_code;210 211	isc->scaler_format[ISC_SCALER_PAD_SINK] =212		 isc->scaler_format[ISC_SCALER_PAD_SOURCE];213 214	ret = media_entity_pads_init(&isc->scaler_sd.entity,215				     ISC_SCALER_PADS_NUM,216				     isc->scaler_pads);217	if (ret < 0) {218		dev_err(isc->dev, "scaler sd media entity init failed\n");219		return ret;220	}221 222	ret = v4l2_device_register_subdev(&isc->v4l2_dev, &isc->scaler_sd);223	if (ret < 0) {224		dev_err(isc->dev, "scaler sd failed to register subdev\n");225		return ret;226	}227 228	return ret;229}230EXPORT_SYMBOL_GPL(isc_scaler_init);231 232int isc_scaler_link(struct isc_device *isc)233{234	int ret;235 236	ret = media_create_pad_link(&isc->current_subdev->sd->entity,237				    isc->remote_pad, &isc->scaler_sd.entity,238				    ISC_SCALER_PAD_SINK,239				    MEDIA_LNK_FL_ENABLED |240				    MEDIA_LNK_FL_IMMUTABLE);241 242	if (ret < 0) {243		dev_err(isc->dev, "Failed to create pad link: %s to %s\n",244			isc->current_subdev->sd->entity.name,245			isc->scaler_sd.entity.name);246		return ret;247	}248 249	dev_dbg(isc->dev, "link with %s pad: %d\n",250		isc->current_subdev->sd->name, isc->remote_pad);251 252	ret = media_create_pad_link(&isc->scaler_sd.entity,253				    ISC_SCALER_PAD_SOURCE,254				    &isc->video_dev.entity, ISC_PAD_SINK,255				    MEDIA_LNK_FL_ENABLED |256				    MEDIA_LNK_FL_IMMUTABLE);257 258	if (ret < 0) {259		dev_err(isc->dev, "Failed to create pad link: %s to %s\n",260			isc->scaler_sd.entity.name,261			isc->video_dev.entity.name);262		return ret;263	}264 265	dev_dbg(isc->dev, "link with %s pad: %d\n", isc->scaler_sd.name,266		ISC_SCALER_PAD_SOURCE);267 268	return ret;269}270EXPORT_SYMBOL_GPL(isc_scaler_link);271 272