brintos

brintos / linux-shallow public Read only

0
0
Text · 11.7 KiB · 62c5c33 Raw
441 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/v4l2-mediabus.h>12#include <media/v4l2-event.h>13#include <media/v4l2-subdev.h>14 15#include "dcmipp-common.h"16 17#define DCMIPP_PRCR	0x10418#define DCMIPP_PRCR_FORMAT_SHIFT	1619#define DCMIPP_PRCR_FORMAT_YUV422	0x1e20#define DCMIPP_PRCR_FORMAT_RGB565	0x2221#define DCMIPP_PRCR_FORMAT_RAW8		0x2a22#define DCMIPP_PRCR_FORMAT_G8		0x4a23#define DCMIPP_PRCR_FORMAT_BYTE_STREAM	0x5a24#define DCMIPP_PRCR_ESS			BIT(4)25#define DCMIPP_PRCR_PCKPOL		BIT(5)26#define DCMIPP_PRCR_HSPOL		BIT(6)27#define DCMIPP_PRCR_VSPOL		BIT(7)28#define DCMIPP_PRCR_ENABLE		BIT(14)29#define DCMIPP_PRCR_SWAPCYCLES		BIT(25)30 31#define DCMIPP_PRESCR	0x10832#define DCMIPP_PRESUR	0x10c33 34#define IS_SINK(pad) (!(pad))35#define IS_SRC(pad)  ((pad))36 37struct dcmipp_par_pix_map {38	unsigned int code_sink;39	unsigned int code_src;40	u8 prcr_format;41	u8 prcr_swapcycles;42};43 44#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap)	\45	{							\46		.code_sink = MEDIA_BUS_FMT_##sink,		\47		.code_src = MEDIA_BUS_FMT_##src,		\48		.prcr_format = DCMIPP_PRCR_FORMAT_##prcr,	\49		.prcr_swapcycles = swap,			\50	}51static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = {52	/* RGB565 */53	PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1),54	PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0),55	/* YUV422 */56	PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1),57	PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0),58	PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1),59	PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0),60	PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1),61	PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1),62	/* GREY */63	PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0),64	/* Raw Bayer */65	PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0),66	PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0),67	PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0),68	PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0),69	/* JPEG */70	PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0),71};72 73/*74 * Search through the pix_map table, skipping two consecutive entry with the75 * same code76 */77static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index78						(unsigned int index,79						 unsigned int pad)80{81	unsigned int i = 0;82	u32 prev_code = 0, cur_code;83 84	while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) {85		if (IS_SRC(pad))86			cur_code = dcmipp_par_pix_map_list[i].code_src;87		else88			cur_code = dcmipp_par_pix_map_list[i].code_sink;89 90		if (cur_code == prev_code) {91			i++;92			continue;93		}94		prev_code = cur_code;95 96		if (index == 0)97			break;98		i++;99		index--;100	}101 102	if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list))103		return NULL;104 105	return &dcmipp_par_pix_map_list[i];106}107 108static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code109					(u32 code_sink, u32 code_src)110{111	unsigned int i;112 113	for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) {114		if ((dcmipp_par_pix_map_list[i].code_sink == code_sink &&115		     dcmipp_par_pix_map_list[i].code_src == code_src) ||116		    (dcmipp_par_pix_map_list[i].code_sink == code_src &&117		     dcmipp_par_pix_map_list[i].code_src == code_sink) ||118		    (dcmipp_par_pix_map_list[i].code_sink == code_sink &&119		     code_src == 0) ||120		    (code_sink == 0 &&121		     dcmipp_par_pix_map_list[i].code_src == code_src))122			return &dcmipp_par_pix_map_list[i];123	}124	return NULL;125}126 127struct dcmipp_par_device {128	struct dcmipp_ent_device ved;129	struct v4l2_subdev sd;130	struct device *dev;131	void __iomem *regs;132	bool streaming;133};134 135static const struct v4l2_mbus_framefmt fmt_default = {136	.width = DCMIPP_FMT_WIDTH_DEFAULT,137	.height = DCMIPP_FMT_HEIGHT_DEFAULT,138	.code = MEDIA_BUS_FMT_RGB565_2X8_LE,139	.field = V4L2_FIELD_NONE,140	.colorspace = DCMIPP_COLORSPACE_DEFAULT,141	.ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,142	.quantization = DCMIPP_QUANTIZATION_DEFAULT,143	.xfer_func = DCMIPP_XFER_FUNC_DEFAULT,144};145 146static int dcmipp_par_init_state(struct v4l2_subdev *sd,147				 struct v4l2_subdev_state *sd_state)148{149	unsigned int i;150 151	for (i = 0; i < sd->entity.num_pads; i++) {152		struct v4l2_mbus_framefmt *mf;153 154		mf = v4l2_subdev_state_get_format(sd_state, i);155		*mf = fmt_default;156	}157 158	return 0;159}160 161static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd,162				     struct v4l2_subdev_state *sd_state,163				     struct v4l2_subdev_mbus_code_enum *code)164{165	const struct dcmipp_par_pix_map *vpix =166		dcmipp_par_pix_map_by_index(code->index, code->pad);167 168	if (!vpix)169		return -EINVAL;170 171	code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink;172 173	return 0;174}175 176static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd,177				      struct v4l2_subdev_state *sd_state,178				      struct v4l2_subdev_frame_size_enum *fse)179{180	const struct dcmipp_par_pix_map *vpix;181 182	if (fse->index)183		return -EINVAL;184 185	/* Only accept code in the pix map table */186	vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0,187					  IS_SRC(fse->pad) ? fse->code : 0);188	if (!vpix)189		return -EINVAL;190 191	fse->min_width = DCMIPP_FRAME_MIN_WIDTH;192	fse->max_width = DCMIPP_FRAME_MAX_WIDTH;193	fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;194	fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;195 196	return 0;197}198 199static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par,200				  struct v4l2_mbus_framefmt *fmt, __u32 pad)201{202	const struct dcmipp_par_pix_map *vpix;203 204	/* Only accept code in the pix map table */205	vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0,206					  IS_SRC(pad) ? fmt->code : 0);207	if (!vpix)208		fmt->code = fmt_default.code;209 210	/* Exclude JPEG if BT656 bus is selected */211	if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 &&212	    par->ved.bus_type == V4L2_MBUS_BT656)213		fmt->code = fmt_default.code;214 215	fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,216			     DCMIPP_FRAME_MAX_WIDTH) & ~1;217	fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,218			      DCMIPP_FRAME_MAX_HEIGHT) & ~1;219 220	if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)221		fmt->field = fmt_default.field;222 223	dcmipp_colorimetry_clamp(fmt);224}225 226static int dcmipp_par_set_fmt(struct v4l2_subdev *sd,227			      struct v4l2_subdev_state *sd_state,228			      struct v4l2_subdev_format *fmt)229{230	struct dcmipp_par_device *par = v4l2_get_subdevdata(sd);231	struct v4l2_mbus_framefmt *mf;232 233	if (par->streaming)234		return -EBUSY;235 236	mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);237 238	/* Set the new format */239	dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad);240 241	dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n",242		par->sd.name,243		/* old */244		mf->width, mf->height, mf->code,245		mf->colorspace,	mf->quantization,246		mf->xfer_func, mf->ycbcr_enc,247		/* new */248		fmt->format.width, fmt->format.height, fmt->format.code,249		fmt->format.colorspace, fmt->format.quantization,250		fmt->format.xfer_func, fmt->format.ycbcr_enc);251 252	*mf = fmt->format;253 254	/* When setting the sink format, report that format on the src pad */255	if (IS_SINK(fmt->pad)) {256		mf = v4l2_subdev_state_get_format(sd_state, 1);257		*mf = fmt->format;258		dcmipp_par_adjust_fmt(par, mf, 1);259	}260 261	return 0;262}263 264static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = {265	.enum_mbus_code		= dcmipp_par_enum_mbus_code,266	.enum_frame_size	= dcmipp_par_enum_frame_size,267	.get_fmt		= v4l2_subdev_get_fmt,268	.set_fmt		= dcmipp_par_set_fmt,269};270 271static int dcmipp_par_configure(struct dcmipp_par_device *par)272{273	u32 val = 0;274	const struct dcmipp_par_pix_map *vpix;275	struct v4l2_subdev_state *state;276	struct v4l2_mbus_framefmt *sink_fmt;277	struct v4l2_mbus_framefmt *src_fmt;278 279	/* Set vertical synchronization polarity */280	if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)281		val |= DCMIPP_PRCR_VSPOL;282 283	/* Set horizontal synchronization polarity */284	if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)285		val |= DCMIPP_PRCR_HSPOL;286 287	/* Set pixel clock polarity */288	if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)289		val |= DCMIPP_PRCR_PCKPOL;290 291	/*292	 * BT656 embedded synchronisation bus mode.293	 *294	 * Default SAV/EAV mode is supported here with default codes295	 * SAV=0xff000080 & EAV=0xff00009d.296	 * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d.297	 */298	if (par->ved.bus_type == V4L2_MBUS_BT656) {299		val |= DCMIPP_PRCR_ESS;300 301		/* Unmask all codes */302		reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */303 304		/* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */305		reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */306	}307 308	/* Set format */309	state = v4l2_subdev_lock_and_get_active_state(&par->sd);310	sink_fmt = v4l2_subdev_state_get_format(state, 0);311	src_fmt = v4l2_subdev_state_get_format(state, 1);312	v4l2_subdev_unlock_state(state);313 314	vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code);315	if (!vpix) {316		dev_err(par->dev, "Invalid sink/src format configuration\n");317		return -EINVAL;318	}319 320	val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT;321 322	/* swap cycles */323	if (vpix->prcr_swapcycles)324		val |= DCMIPP_PRCR_SWAPCYCLES;325 326	reg_write(par, DCMIPP_PRCR, val);327 328	return 0;329}330 331static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable)332{333	struct dcmipp_par_device *par =334				container_of(sd, struct dcmipp_par_device, sd);335	struct v4l2_subdev *s_subdev;336	struct media_pad *pad;337	int ret = 0;338 339	/* Get source subdev */340	pad = media_pad_remote_pad_first(&sd->entity.pads[0]);341	if (!pad || !is_media_entity_v4l2_subdev(pad->entity))342		return -EINVAL;343	s_subdev = media_entity_to_v4l2_subdev(pad->entity);344 345	if (enable) {346		ret = dcmipp_par_configure(par);347		if (ret)348			return ret;349 350		/* Enable parallel interface */351		reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);352 353		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);354		if (ret < 0) {355			dev_err(par->dev,356				"failed to start source subdev streaming (%d)\n",357				ret);358			return ret;359		}360	} else {361		ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);362		if (ret < 0) {363			dev_err(par->dev,364				"failed to stop source subdev streaming (%d)\n",365				ret);366			return ret;367		}368 369		/* Disable parallel interface */370		reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);371	}372 373	par->streaming = enable;374 375	return ret;376}377 378static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = {379	.s_stream = dcmipp_par_s_stream,380};381 382static const struct v4l2_subdev_ops dcmipp_par_ops = {383	.pad = &dcmipp_par_pad_ops,384	.video = &dcmipp_par_video_ops,385};386 387static void dcmipp_par_release(struct v4l2_subdev *sd)388{389	struct dcmipp_par_device *par =390				container_of(sd, struct dcmipp_par_device, sd);391 392	kfree(par);393}394 395static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = {396	.init_state = dcmipp_par_init_state,397	.release = dcmipp_par_release,398};399 400void dcmipp_par_ent_release(struct dcmipp_ent_device *ved)401{402	struct dcmipp_par_device *par =403			container_of(ved, struct dcmipp_par_device, ved);404 405	dcmipp_ent_sd_unregister(ved, &par->sd);406}407 408struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev,409					      const char *entity_name,410					      struct v4l2_device *v4l2_dev,411					      void __iomem *regs)412{413	struct dcmipp_par_device *par;414	const unsigned long pads_flag[] = {415		MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,416	};417	int ret;418 419	/* Allocate the par struct */420	par = kzalloc(sizeof(*par), GFP_KERNEL);421	if (!par)422		return ERR_PTR(-ENOMEM);423 424	par->regs = regs;425 426	/* Initialize ved and sd */427	ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev,428				     entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,429				     ARRAY_SIZE(pads_flag), pads_flag,430				     &dcmipp_par_int_ops, &dcmipp_par_ops,431				     NULL, NULL);432	if (ret) {433		kfree(par);434		return ERR_PTR(ret);435	}436 437	par->dev = dev;438 439	return &par->ved;440}441