brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 5a361ad Raw
566 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for STM32 Digital Camera Memory Interface Pixel Processor4 *5 * Copyright (C) STMicroelectronics SA 20236 * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>7 *          Alain Volmat <alain.volmat@foss.st.com>8 *          for STMicroelectronics.9 */10 11#include <linux/vmalloc.h>12#include <linux/v4l2-mediabus.h>13#include <media/v4l2-rect.h>14#include <media/v4l2-subdev.h>15 16#include "dcmipp-common.h"17 18#define DCMIPP_P0FCTCR	0x50019#define DCMIPP_P0FCTCR_FRATE_MASK	GENMASK(1, 0)20#define DCMIPP_P0SCSTR	0x50421#define DCMIPP_P0SCSTR_HSTART_SHIFT	022#define DCMIPP_P0SCSTR_VSTART_SHIFT	1623#define DCMIPP_P0SCSZR	0x50824#define DCMIPP_P0SCSZR_ENABLE		BIT(31)25#define DCMIPP_P0SCSZR_HSIZE_SHIFT	026#define DCMIPP_P0SCSZR_VSIZE_SHIFT	1627#define DCMIPP_P0PPCR	0x5c028#define DCMIPP_P0PPCR_BSM_1_2		0x129#define DCMIPP_P0PPCR_BSM_1_4		0x230#define DCMIPP_P0PPCR_BSM_2_4		0x331#define DCMIPP_P0PPCR_BSM_MASK		GENMASK(8, 7)32#define DCMIPP_P0PPCR_BSM_SHIFT		0x733#define DCMIPP_P0PPCR_LSM		BIT(10)34#define DCMIPP_P0PPCR_OELS		BIT(11)35 36#define IS_SINK(pad) (!(pad))37#define IS_SRC(pad)  ((pad))38 39struct dcmipp_byteproc_pix_map {40	unsigned int code;41	unsigned int bpp;42};43 44#define PIXMAP_MBUS_BPP(mbus, byteperpixel)		\45	{						\46		.code = MEDIA_BUS_FMT_##mbus,		\47		.bpp = byteperpixel,			\48	}49static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {50	PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2),51	PIXMAP_MBUS_BPP(YUYV8_2X8, 2),52	PIXMAP_MBUS_BPP(YVYU8_2X8, 2),53	PIXMAP_MBUS_BPP(UYVY8_2X8, 2),54	PIXMAP_MBUS_BPP(VYUY8_2X8, 2),55	PIXMAP_MBUS_BPP(Y8_1X8, 1),56	PIXMAP_MBUS_BPP(SBGGR8_1X8, 1),57	PIXMAP_MBUS_BPP(SGBRG8_1X8, 1),58	PIXMAP_MBUS_BPP(SGRBG8_1X8, 1),59	PIXMAP_MBUS_BPP(SRGGB8_1X8, 1),60	PIXMAP_MBUS_BPP(JPEG_1X8, 1),61};62 63static const struct dcmipp_byteproc_pix_map *64dcmipp_byteproc_pix_map_by_code(u32 code)65{66	unsigned int i;67 68	for (i = 0; i < ARRAY_SIZE(dcmipp_byteproc_pix_map_list); i++) {69		if (dcmipp_byteproc_pix_map_list[i].code == code)70			return &dcmipp_byteproc_pix_map_list[i];71	}72 73	return NULL;74}75 76struct dcmipp_byteproc_device {77	struct dcmipp_ent_device ved;78	struct v4l2_subdev sd;79	struct device *dev;80	void __iomem *regs;81	bool streaming;82};83 84static const struct v4l2_mbus_framefmt fmt_default = {85	.width = DCMIPP_FMT_WIDTH_DEFAULT,86	.height = DCMIPP_FMT_HEIGHT_DEFAULT,87	.code = MEDIA_BUS_FMT_RGB565_2X8_LE,88	.field = V4L2_FIELD_NONE,89	.colorspace = DCMIPP_COLORSPACE_DEFAULT,90	.ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,91	.quantization = DCMIPP_QUANTIZATION_DEFAULT,92	.xfer_func = DCMIPP_XFER_FUNC_DEFAULT,93};94 95static const struct v4l2_rect crop_min = {96	.width = DCMIPP_FRAME_MIN_WIDTH,97	.height = DCMIPP_FRAME_MIN_HEIGHT,98	.top = 0,99	.left = 0,100};101 102static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r,103					struct v4l2_rect *compose)104{105	/* Disallow rectangles smaller than the minimal one. */106	v4l2_rect_set_min_size(r, &crop_min);107	v4l2_rect_map_inside(r, compose);108}109 110static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r,111					   const struct v4l2_mbus_framefmt *fmt)112{113	r->top = 0;114	r->left = 0;115 116	/* Compose is not possible for JPEG or Bayer formats */117	if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 ||118	    fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||119	    fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 ||120	    fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 ||121	    fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) {122		r->width = fmt->width;123		r->height = fmt->height;124		return;125	}126 127	/* Adjust height - we can only perform 1/2 decimation */128	if (r->height <= (fmt->height / 2))129		r->height = fmt->height / 2;130	else131		r->height = fmt->height;132 133	/* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */134	if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4))135		r->width = fmt->width / 4;136	else if (r->width <= (fmt->width / 2))137		r->width = fmt->width / 2;138	else139		r->width = fmt->width;140}141 142static void dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt *fmt)143{144	const struct dcmipp_byteproc_pix_map *vpix;145 146	/* Only accept code in the pix map table */147	vpix = dcmipp_byteproc_pix_map_by_code(fmt->code);148	if (!vpix)149		fmt->code = fmt_default.code;150 151	fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,152			     DCMIPP_FRAME_MAX_WIDTH) & ~1;153	fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,154			      DCMIPP_FRAME_MAX_HEIGHT) & ~1;155 156	if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)157		fmt->field = fmt_default.field;158 159	dcmipp_colorimetry_clamp(fmt);160}161 162static int dcmipp_byteproc_init_state(struct v4l2_subdev *sd,163				      struct v4l2_subdev_state *sd_state)164{165	unsigned int i;166 167	for (i = 0; i < sd->entity.num_pads; i++) {168		struct v4l2_mbus_framefmt *mf;169		struct v4l2_rect *r;170 171		mf = v4l2_subdev_state_get_format(sd_state, i);172		*mf = fmt_default;173 174		if (IS_SINK(i))175			r = v4l2_subdev_state_get_compose(sd_state, i);176		else177			r = v4l2_subdev_state_get_crop(sd_state, i);178 179		r->top = 0;180		r->left = 0;181		r->width = DCMIPP_FMT_WIDTH_DEFAULT;182		r->height = DCMIPP_FMT_HEIGHT_DEFAULT;183	}184 185	return 0;186}187 188static int189dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev *sd,190			       struct v4l2_subdev_state *sd_state,191			       struct v4l2_subdev_mbus_code_enum *code)192{193	const struct dcmipp_byteproc_pix_map *vpix;194	struct v4l2_mbus_framefmt *sink_fmt;195 196	if (IS_SINK(code->pad)) {197		if (code->index >= ARRAY_SIZE(dcmipp_byteproc_pix_map_list))198			return -EINVAL;199		vpix = &dcmipp_byteproc_pix_map_list[code->index];200		code->code = vpix->code;201	} else {202		/* byteproc doesn't support transformation on format */203		if (code->index > 0)204			return -EINVAL;205 206		sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);207		code->code = sink_fmt->code;208	}209 210	return 0;211}212 213static int214dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd,215				struct v4l2_subdev_state *sd_state,216				struct v4l2_subdev_frame_size_enum *fse)217{218	struct v4l2_rect *compose;219 220	if (fse->index)221		return -EINVAL;222 223	fse->min_width = DCMIPP_FRAME_MIN_WIDTH;224	fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;225 226	if (IS_SINK(fse->pad)) {227		fse->max_width = DCMIPP_FRAME_MAX_WIDTH;228		fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;229	} else {230		compose = v4l2_subdev_state_get_compose(sd_state, 0);231		fse->max_width = compose->width;232		fse->max_height = compose->height;233	}234 235	return 0;236}237 238static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,239				   struct v4l2_subdev_state *sd_state,240				   struct v4l2_subdev_format *fmt)241{242	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);243	struct v4l2_mbus_framefmt *mf;244	struct v4l2_rect *crop, *compose;245 246	if (byteproc->streaming)247		return -EBUSY;248 249	mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);250 251	crop = v4l2_subdev_state_get_crop(sd_state, 1);252	compose = v4l2_subdev_state_get_compose(sd_state, 0);253 254	if (IS_SRC(fmt->pad)) {255		fmt->format = *v4l2_subdev_state_get_format(sd_state, 0);256		fmt->format.width = crop->width;257		fmt->format.height = crop->height;258	} else {259		dcmipp_byteproc_adjust_fmt(&fmt->format);260		crop->top = 0;261		crop->left = 0;262		crop->width = fmt->format.width;263		crop->height = fmt->format.height;264		*compose = *crop;265		/* Set the same format on SOURCE pad as well */266		*v4l2_subdev_state_get_format(sd_state, 1) = fmt->format;267	}268	*mf = fmt->format;269 270	return 0;271}272 273static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd,274					 struct v4l2_subdev_state *sd_state,275					 struct v4l2_subdev_selection *s)276{277	struct v4l2_mbus_framefmt *sink_fmt;278	struct v4l2_rect *crop, *compose;279 280	/*281	 * In the HW, the decimation block is located prior to the crop hence:282	 * Compose is done on the sink pad283	 * Crop is done on the src pad284	 */285	if (IS_SINK(s->pad) &&286	    (s->target == V4L2_SEL_TGT_CROP ||287	     s->target == V4L2_SEL_TGT_CROP_BOUNDS ||288	     s->target == V4L2_SEL_TGT_CROP_DEFAULT))289		return -EINVAL;290 291	if (IS_SRC(s->pad) &&292	    (s->target == V4L2_SEL_TGT_COMPOSE ||293	     s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||294	     s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT))295		return -EINVAL;296 297	sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);298	crop = v4l2_subdev_state_get_crop(sd_state, 1);299	compose = v4l2_subdev_state_get_compose(sd_state, 0);300 301	switch (s->target) {302	case V4L2_SEL_TGT_CROP:303		s->r = *crop;304		break;305	case V4L2_SEL_TGT_CROP_BOUNDS:306	case V4L2_SEL_TGT_CROP_DEFAULT:307		s->r = *compose;308		break;309	case V4L2_SEL_TGT_COMPOSE:310		s->r = *compose;311		break;312	case V4L2_SEL_TGT_COMPOSE_BOUNDS:313	case V4L2_SEL_TGT_COMPOSE_DEFAULT:314		s->r.top = 0;315		s->r.left = 0;316		s->r.width = sink_fmt->width;317		s->r.height = sink_fmt->height;318		break;319	default:320		return -EINVAL;321	}322 323	return 0;324}325 326static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,327					 struct v4l2_subdev_state *sd_state,328					 struct v4l2_subdev_selection *s)329{330	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);331	struct v4l2_mbus_framefmt *mf;332	struct v4l2_rect *crop, *compose;333 334	/*335	 * In the HW, the decimation block is located prior to the crop hence:336	 * Compose is done on the sink pad337	 * Crop is done on the src pad338	 */339	if ((s->target == V4L2_SEL_TGT_CROP ||340	     s->target == V4L2_SEL_TGT_CROP_BOUNDS ||341	     s->target == V4L2_SEL_TGT_CROP_DEFAULT) && IS_SINK(s->pad))342		return -EINVAL;343 344	if ((s->target == V4L2_SEL_TGT_COMPOSE ||345	     s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||346	     s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT) && IS_SRC(s->pad))347		return -EINVAL;348 349	crop = v4l2_subdev_state_get_crop(sd_state, 1);350	compose = v4l2_subdev_state_get_compose(sd_state, 0);351 352	switch (s->target) {353	case V4L2_SEL_TGT_CROP:354		dcmipp_byteproc_adjust_crop(&s->r, compose);355 356		*crop = s->r;357		mf = v4l2_subdev_state_get_format(sd_state, 1);358		mf->width = s->r.width;359		mf->height = s->r.height;360 361		dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n",362			crop->width, crop->height, crop->left, crop->top);363		break;364	case V4L2_SEL_TGT_COMPOSE:365		mf = v4l2_subdev_state_get_format(sd_state, 0);366		dcmipp_byteproc_adjust_compose(&s->r, mf);367		*compose = s->r;368		*crop = s->r;369 370		mf = v4l2_subdev_state_get_format(sd_state, 1);371		mf->width = s->r.width;372		mf->height = s->r.height;373 374		dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n",375			compose->width, compose->height,376			compose->left, compose->top);377		break;378	default:379		return -EINVAL;380	}381 382	return 0;383}384 385static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = {386	.enum_mbus_code		= dcmipp_byteproc_enum_mbus_code,387	.enum_frame_size	= dcmipp_byteproc_enum_frame_size,388	.get_fmt		= v4l2_subdev_get_fmt,389	.set_fmt		= dcmipp_byteproc_set_fmt,390	.get_selection		= dcmipp_byteproc_get_selection,391	.set_selection		= dcmipp_byteproc_set_selection,392};393 394static int dcmipp_byteproc_configure_scale_crop395			(struct dcmipp_byteproc_device *byteproc)396{397	const struct dcmipp_byteproc_pix_map *vpix;398	struct v4l2_subdev_state *state;399	struct v4l2_mbus_framefmt *sink_fmt;400	u32 hprediv, vprediv;401	struct v4l2_rect *compose, *crop;402	u32 val = 0;403 404	state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd);405	sink_fmt = v4l2_subdev_state_get_format(state, 0);406	compose = v4l2_subdev_state_get_compose(state, 0);407	crop = v4l2_subdev_state_get_crop(state, 1);408	v4l2_subdev_unlock_state(state);409 410	/* find output format bpp */411	vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code);412	if (!vpix)413		return -EINVAL;414 415	/* clear decimation/crop */416	reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK);417	reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM);418	reg_write(byteproc, DCMIPP_P0SCSTR, 0);419	reg_write(byteproc, DCMIPP_P0SCSZR, 0);420 421	/* Ignore decimation/crop with JPEG */422	if (vpix->code == MEDIA_BUS_FMT_JPEG_1X8)423		return 0;424 425	/* decimation */426	hprediv = sink_fmt->width / compose->width;427	if (hprediv == 4)428		val |= DCMIPP_P0PPCR_BSM_1_4 << DCMIPP_P0PPCR_BSM_SHIFT;429	else if ((vpix->code == MEDIA_BUS_FMT_Y8_1X8) && (hprediv == 2))430		val |= DCMIPP_P0PPCR_BSM_1_2 << DCMIPP_P0PPCR_BSM_SHIFT;431	else if (hprediv == 2)432		val |= DCMIPP_P0PPCR_BSM_2_4 << DCMIPP_P0PPCR_BSM_SHIFT;433 434	vprediv = sink_fmt->height / compose->height;435	if (vprediv == 2)436		val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;437 438	/* decimate using bytes and lines skipping */439	if (val) {440		reg_set(byteproc, DCMIPP_P0PPCR, val);441 442		dev_dbg(byteproc->dev, "decimate to %dx%d [prediv=%dx%d]\n",443			compose->width, compose->height,444			hprediv, vprediv);445	}446 447	dev_dbg(byteproc->dev, "crop to %dx%d\n", crop->width, crop->height);448 449	/* expressed in 32-bits words on X axis, lines on Y axis */450	reg_write(byteproc, DCMIPP_P0SCSTR,451		  (((crop->left * vpix->bpp) / 4) <<452		   DCMIPP_P0SCSTR_HSTART_SHIFT) |453		  (crop->top << DCMIPP_P0SCSTR_VSTART_SHIFT));454	reg_write(byteproc, DCMIPP_P0SCSZR,455		  DCMIPP_P0SCSZR_ENABLE |456		  (((crop->width * vpix->bpp) / 4) <<457		   DCMIPP_P0SCSZR_HSIZE_SHIFT) |458		  (crop->height << DCMIPP_P0SCSZR_VSIZE_SHIFT));459 460	return 0;461}462 463static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable)464{465	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);466	struct v4l2_subdev *s_subdev;467	struct media_pad *pad;468	int ret = 0;469 470	/* Get source subdev */471	pad = media_pad_remote_pad_first(&sd->entity.pads[0]);472	if (!pad || !is_media_entity_v4l2_subdev(pad->entity))473		return -EINVAL;474	s_subdev = media_entity_to_v4l2_subdev(pad->entity);475 476	if (enable) {477		ret = dcmipp_byteproc_configure_scale_crop(byteproc);478		if (ret)479			return ret;480 481		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);482		if (ret < 0) {483			dev_err(byteproc->dev,484				"failed to start source subdev streaming (%d)\n",485				ret);486			return ret;487		}488	} else {489		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);490		if (ret < 0) {491			dev_err(byteproc->dev,492				"failed to stop source subdev streaming (%d)\n",493				ret);494			return ret;495		}496	}497 498	byteproc->streaming = enable;499 500	return 0;501}502 503static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = {504	.s_stream = dcmipp_byteproc_s_stream,505};506 507static const struct v4l2_subdev_ops dcmipp_byteproc_ops = {508	.pad = &dcmipp_byteproc_pad_ops,509	.video = &dcmipp_byteproc_video_ops,510};511 512static void dcmipp_byteproc_release(struct v4l2_subdev *sd)513{514	struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);515 516	kfree(byteproc);517}518 519static const struct v4l2_subdev_internal_ops dcmipp_byteproc_int_ops = {520	.init_state = dcmipp_byteproc_init_state,521	.release = dcmipp_byteproc_release,522};523 524void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved)525{526	struct dcmipp_byteproc_device *byteproc =527			container_of(ved, struct dcmipp_byteproc_device, ved);528 529	dcmipp_ent_sd_unregister(ved, &byteproc->sd);530}531 532struct dcmipp_ent_device *533dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,534			 struct v4l2_device *v4l2_dev, void __iomem *regs)535{536	struct dcmipp_byteproc_device *byteproc;537	const unsigned long pads_flag[] = {538		MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,539	};540	int ret;541 542	/* Allocate the byteproc struct */543	byteproc = kzalloc(sizeof(*byteproc), GFP_KERNEL);544	if (!byteproc)545		return ERR_PTR(-ENOMEM);546 547	byteproc->regs = regs;548 549	/* Initialize ved and sd */550	ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd,551				     v4l2_dev, entity_name,552				     MEDIA_ENT_F_PROC_VIDEO_SCALER,553				     ARRAY_SIZE(pads_flag), pads_flag,554				     &dcmipp_byteproc_int_ops,555				     &dcmipp_byteproc_ops,556				     NULL, NULL);557	if (ret) {558		kfree(byteproc);559		return ERR_PTR(ret);560	}561 562	byteproc->dev = dev;563 564	return &byteproc->ved;565}566