cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

imx-ic-prp.c (11579B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC
      4 *
      5 * This subdevice handles capture of video frames from the CSI or VDIC,
      6 * which are routed directly to the Image Converter preprocess tasks,
      7 * for resizing, colorspace conversion, and rotation.
      8 *
      9 * Copyright (c) 2012-2017 Mentor Graphics Inc.
     10 */
     11#include <linux/delay.h>
     12#include <linux/interrupt.h>
     13#include <linux/module.h>
     14#include <linux/sched.h>
     15#include <linux/slab.h>
     16#include <linux/spinlock.h>
     17#include <linux/timer.h>
     18#include <media/v4l2-ctrls.h>
     19#include <media/v4l2-device.h>
     20#include <media/v4l2-ioctl.h>
     21#include <media/v4l2-subdev.h>
     22#include <media/imx.h>
     23#include "imx-media.h"
     24#include "imx-ic.h"
     25
     26/*
     27 * Min/Max supported width and heights.
     28 */
     29#define MIN_W        32
     30#define MIN_H        32
     31#define MAX_W      4096
     32#define MAX_H      4096
     33#define W_ALIGN    4 /* multiple of 16 pixels */
     34#define H_ALIGN    1 /* multiple of 2 lines */
     35#define S_ALIGN    1 /* multiple of 2 */
     36
     37struct prp_priv {
     38	struct imx_ic_priv *ic_priv;
     39	struct media_pad pad[PRP_NUM_PADS];
     40
     41	/* lock to protect all members below */
     42	struct mutex lock;
     43
     44	struct v4l2_subdev *src_sd;
     45	struct v4l2_subdev *sink_sd_prpenc;
     46	struct v4l2_subdev *sink_sd_prpvf;
     47
     48	/* the CSI id at link validate */
     49	int csi_id;
     50
     51	struct v4l2_mbus_framefmt format_mbus;
     52	struct v4l2_fract frame_interval;
     53
     54	int stream_count;
     55};
     56
     57static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd)
     58{
     59	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
     60
     61	return ic_priv->task_priv;
     62}
     63
     64static int prp_start(struct prp_priv *priv)
     65{
     66	struct imx_ic_priv *ic_priv = priv->ic_priv;
     67	bool src_is_vdic;
     68
     69	/* set IC to receive from CSI or VDI depending on source */
     70	src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC);
     71
     72	ipu_set_ic_src_mux(ic_priv->ipu, priv->csi_id, src_is_vdic);
     73
     74	return 0;
     75}
     76
     77static void prp_stop(struct prp_priv *priv)
     78{
     79}
     80
     81static struct v4l2_mbus_framefmt *
     82__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state,
     83	      unsigned int pad, enum v4l2_subdev_format_whence which)
     84{
     85	struct imx_ic_priv *ic_priv = priv->ic_priv;
     86
     87	if (which == V4L2_SUBDEV_FORMAT_TRY)
     88		return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad);
     89	else
     90		return &priv->format_mbus;
     91}
     92
     93/*
     94 * V4L2 subdev operations.
     95 */
     96
     97static int prp_enum_mbus_code(struct v4l2_subdev *sd,
     98			      struct v4l2_subdev_state *sd_state,
     99			      struct v4l2_subdev_mbus_code_enum *code)
    100{
    101	struct prp_priv *priv = sd_to_priv(sd);
    102	struct v4l2_mbus_framefmt *infmt;
    103	int ret = 0;
    104
    105	mutex_lock(&priv->lock);
    106
    107	switch (code->pad) {
    108	case PRP_SINK_PAD:
    109		ret = imx_media_enum_ipu_formats(&code->code, code->index,
    110						 PIXFMT_SEL_YUV_RGB);
    111		break;
    112	case PRP_SRC_PAD_PRPENC:
    113	case PRP_SRC_PAD_PRPVF:
    114		if (code->index != 0) {
    115			ret = -EINVAL;
    116			goto out;
    117		}
    118		infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD,
    119				      code->which);
    120		code->code = infmt->code;
    121		break;
    122	default:
    123		ret = -EINVAL;
    124	}
    125out:
    126	mutex_unlock(&priv->lock);
    127	return ret;
    128}
    129
    130static int prp_get_fmt(struct v4l2_subdev *sd,
    131		       struct v4l2_subdev_state *sd_state,
    132		       struct v4l2_subdev_format *sdformat)
    133{
    134	struct prp_priv *priv = sd_to_priv(sd);
    135	struct v4l2_mbus_framefmt *fmt;
    136	int ret = 0;
    137
    138	if (sdformat->pad >= PRP_NUM_PADS)
    139		return -EINVAL;
    140
    141	mutex_lock(&priv->lock);
    142
    143	fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
    144	if (!fmt) {
    145		ret = -EINVAL;
    146		goto out;
    147	}
    148
    149	sdformat->format = *fmt;
    150out:
    151	mutex_unlock(&priv->lock);
    152	return ret;
    153}
    154
    155static int prp_set_fmt(struct v4l2_subdev *sd,
    156		       struct v4l2_subdev_state *sd_state,
    157		       struct v4l2_subdev_format *sdformat)
    158{
    159	struct prp_priv *priv = sd_to_priv(sd);
    160	struct v4l2_mbus_framefmt *fmt, *infmt;
    161	const struct imx_media_pixfmt *cc;
    162	int ret = 0;
    163	u32 code;
    164
    165	if (sdformat->pad >= PRP_NUM_PADS)
    166		return -EINVAL;
    167
    168	mutex_lock(&priv->lock);
    169
    170	if (priv->stream_count > 0) {
    171		ret = -EBUSY;
    172		goto out;
    173	}
    174
    175	infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which);
    176
    177	switch (sdformat->pad) {
    178	case PRP_SINK_PAD:
    179		v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
    180				      W_ALIGN, &sdformat->format.height,
    181				      MIN_H, MAX_H, H_ALIGN, S_ALIGN);
    182
    183		cc = imx_media_find_ipu_format(sdformat->format.code,
    184					       PIXFMT_SEL_YUV_RGB);
    185		if (!cc) {
    186			imx_media_enum_ipu_formats(&code, 0,
    187						   PIXFMT_SEL_YUV_RGB);
    188			cc = imx_media_find_ipu_format(code,
    189						       PIXFMT_SEL_YUV_RGB);
    190			sdformat->format.code = cc->codes[0];
    191		}
    192
    193		if (sdformat->format.field == V4L2_FIELD_ANY)
    194			sdformat->format.field = V4L2_FIELD_NONE;
    195		break;
    196	case PRP_SRC_PAD_PRPENC:
    197	case PRP_SRC_PAD_PRPVF:
    198		/* Output pads mirror input pad */
    199		sdformat->format = *infmt;
    200		break;
    201	}
    202
    203	imx_media_try_colorimetry(&sdformat->format, true);
    204
    205	fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which);
    206	*fmt = sdformat->format;
    207out:
    208	mutex_unlock(&priv->lock);
    209	return ret;
    210}
    211
    212static int prp_link_setup(struct media_entity *entity,
    213			  const struct media_pad *local,
    214			  const struct media_pad *remote, u32 flags)
    215{
    216	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
    217	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
    218	struct prp_priv *priv = ic_priv->task_priv;
    219	struct v4l2_subdev *remote_sd;
    220	int ret = 0;
    221
    222	dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s",
    223		ic_priv->sd.name, remote->entity->name, local->entity->name);
    224
    225	remote_sd = media_entity_to_v4l2_subdev(remote->entity);
    226
    227	mutex_lock(&priv->lock);
    228
    229	if (local->flags & MEDIA_PAD_FL_SINK) {
    230		if (flags & MEDIA_LNK_FL_ENABLED) {
    231			if (priv->src_sd) {
    232				ret = -EBUSY;
    233				goto out;
    234			}
    235			if (priv->sink_sd_prpenc &&
    236			    (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) {
    237				ret = -EINVAL;
    238				goto out;
    239			}
    240			priv->src_sd = remote_sd;
    241		} else {
    242			priv->src_sd = NULL;
    243		}
    244
    245		goto out;
    246	}
    247
    248	/* this is a source pad */
    249	if (flags & MEDIA_LNK_FL_ENABLED) {
    250		switch (local->index) {
    251		case PRP_SRC_PAD_PRPENC:
    252			if (priv->sink_sd_prpenc) {
    253				ret = -EBUSY;
    254				goto out;
    255			}
    256			if (priv->src_sd && (priv->src_sd->grp_id &
    257					     IMX_MEDIA_GRP_ID_IPU_VDIC)) {
    258				ret = -EINVAL;
    259				goto out;
    260			}
    261			priv->sink_sd_prpenc = remote_sd;
    262			break;
    263		case PRP_SRC_PAD_PRPVF:
    264			if (priv->sink_sd_prpvf) {
    265				ret = -EBUSY;
    266				goto out;
    267			}
    268			priv->sink_sd_prpvf = remote_sd;
    269			break;
    270		default:
    271			ret = -EINVAL;
    272		}
    273	} else {
    274		switch (local->index) {
    275		case PRP_SRC_PAD_PRPENC:
    276			priv->sink_sd_prpenc = NULL;
    277			break;
    278		case PRP_SRC_PAD_PRPVF:
    279			priv->sink_sd_prpvf = NULL;
    280			break;
    281		default:
    282			ret = -EINVAL;
    283		}
    284	}
    285
    286out:
    287	mutex_unlock(&priv->lock);
    288	return ret;
    289}
    290
    291static int prp_link_validate(struct v4l2_subdev *sd,
    292			     struct media_link *link,
    293			     struct v4l2_subdev_format *source_fmt,
    294			     struct v4l2_subdev_format *sink_fmt)
    295{
    296	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
    297	struct prp_priv *priv = ic_priv->task_priv;
    298	struct v4l2_subdev *csi;
    299	int ret;
    300
    301	ret = v4l2_subdev_link_validate_default(sd, link,
    302						source_fmt, sink_fmt);
    303	if (ret)
    304		return ret;
    305
    306	csi = imx_media_pipeline_subdev(&ic_priv->sd.entity,
    307					IMX_MEDIA_GRP_ID_IPU_CSI, true);
    308	if (IS_ERR(csi))
    309		csi = NULL;
    310
    311	mutex_lock(&priv->lock);
    312
    313	if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) {
    314		/*
    315		 * the ->PRPENC link cannot be enabled if the source
    316		 * is the VDIC
    317		 */
    318		if (priv->sink_sd_prpenc) {
    319			ret = -EINVAL;
    320			goto out;
    321		}
    322	} else {
    323		/* the source is a CSI */
    324		if (!csi) {
    325			ret = -EINVAL;
    326			goto out;
    327		}
    328	}
    329
    330	if (csi) {
    331		switch (csi->grp_id) {
    332		case IMX_MEDIA_GRP_ID_IPU_CSI0:
    333			priv->csi_id = 0;
    334			break;
    335		case IMX_MEDIA_GRP_ID_IPU_CSI1:
    336			priv->csi_id = 1;
    337			break;
    338		default:
    339			ret = -EINVAL;
    340		}
    341	} else {
    342		priv->csi_id = 0;
    343	}
    344
    345out:
    346	mutex_unlock(&priv->lock);
    347	return ret;
    348}
    349
    350static int prp_s_stream(struct v4l2_subdev *sd, int enable)
    351{
    352	struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
    353	struct prp_priv *priv = ic_priv->task_priv;
    354	int ret = 0;
    355
    356	mutex_lock(&priv->lock);
    357
    358	if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) {
    359		ret = -EPIPE;
    360		goto out;
    361	}
    362
    363	/*
    364	 * enable/disable streaming only if stream_count is
    365	 * going from 0 to 1 / 1 to 0.
    366	 */
    367	if (priv->stream_count != !enable)
    368		goto update_count;
    369
    370	dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name,
    371		enable ? "ON" : "OFF");
    372
    373	if (enable)
    374		ret = prp_start(priv);
    375	else
    376		prp_stop(priv);
    377	if (ret)
    378		goto out;
    379
    380	/* start/stop upstream */
    381	ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable);
    382	ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
    383	if (ret) {
    384		if (enable)
    385			prp_stop(priv);
    386		goto out;
    387	}
    388
    389update_count:
    390	priv->stream_count += enable ? 1 : -1;
    391	if (priv->stream_count < 0)
    392		priv->stream_count = 0;
    393out:
    394	mutex_unlock(&priv->lock);
    395	return ret;
    396}
    397
    398static int prp_g_frame_interval(struct v4l2_subdev *sd,
    399				struct v4l2_subdev_frame_interval *fi)
    400{
    401	struct prp_priv *priv = sd_to_priv(sd);
    402
    403	if (fi->pad >= PRP_NUM_PADS)
    404		return -EINVAL;
    405
    406	mutex_lock(&priv->lock);
    407	fi->interval = priv->frame_interval;
    408	mutex_unlock(&priv->lock);
    409
    410	return 0;
    411}
    412
    413static int prp_s_frame_interval(struct v4l2_subdev *sd,
    414				struct v4l2_subdev_frame_interval *fi)
    415{
    416	struct prp_priv *priv = sd_to_priv(sd);
    417
    418	if (fi->pad >= PRP_NUM_PADS)
    419		return -EINVAL;
    420
    421	mutex_lock(&priv->lock);
    422
    423	/* No limits on valid frame intervals */
    424	if (fi->interval.numerator == 0 || fi->interval.denominator == 0)
    425		fi->interval = priv->frame_interval;
    426	else
    427		priv->frame_interval = fi->interval;
    428
    429	mutex_unlock(&priv->lock);
    430
    431	return 0;
    432}
    433
    434static int prp_registered(struct v4l2_subdev *sd)
    435{
    436	struct prp_priv *priv = sd_to_priv(sd);
    437	u32 code;
    438
    439	/* init default frame interval */
    440	priv->frame_interval.numerator = 1;
    441	priv->frame_interval.denominator = 30;
    442
    443	/* set a default mbus format  */
    444	imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV);
    445
    446	return imx_media_init_mbus_fmt(&priv->format_mbus,
    447				       IMX_MEDIA_DEF_PIX_WIDTH,
    448				       IMX_MEDIA_DEF_PIX_HEIGHT, code,
    449				       V4L2_FIELD_NONE, NULL);
    450}
    451
    452static const struct v4l2_subdev_pad_ops prp_pad_ops = {
    453	.init_cfg = imx_media_init_cfg,
    454	.enum_mbus_code = prp_enum_mbus_code,
    455	.get_fmt = prp_get_fmt,
    456	.set_fmt = prp_set_fmt,
    457	.link_validate = prp_link_validate,
    458};
    459
    460static const struct v4l2_subdev_video_ops prp_video_ops = {
    461	.g_frame_interval = prp_g_frame_interval,
    462	.s_frame_interval = prp_s_frame_interval,
    463	.s_stream = prp_s_stream,
    464};
    465
    466static const struct media_entity_operations prp_entity_ops = {
    467	.link_setup = prp_link_setup,
    468	.link_validate = v4l2_subdev_link_validate,
    469};
    470
    471static const struct v4l2_subdev_ops prp_subdev_ops = {
    472	.video = &prp_video_ops,
    473	.pad = &prp_pad_ops,
    474};
    475
    476static const struct v4l2_subdev_internal_ops prp_internal_ops = {
    477	.registered = prp_registered,
    478};
    479
    480static int prp_init(struct imx_ic_priv *ic_priv)
    481{
    482	struct prp_priv *priv;
    483	int i;
    484
    485	priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL);
    486	if (!priv)
    487		return -ENOMEM;
    488
    489	mutex_init(&priv->lock);
    490	ic_priv->task_priv = priv;
    491	priv->ic_priv = ic_priv;
    492
    493	for (i = 0; i < PRP_NUM_PADS; i++)
    494		priv->pad[i].flags = (i == PRP_SINK_PAD) ?
    495			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
    496
    497	return media_entity_pads_init(&ic_priv->sd.entity, PRP_NUM_PADS,
    498				      priv->pad);
    499}
    500
    501static void prp_remove(struct imx_ic_priv *ic_priv)
    502{
    503	struct prp_priv *priv = ic_priv->task_priv;
    504
    505	mutex_destroy(&priv->lock);
    506}
    507
    508struct imx_ic_ops imx_ic_prp_ops = {
    509	.subdev_ops = &prp_subdev_ops,
    510	.internal_ops = &prp_internal_ops,
    511	.entity_ops = &prp_entity_ops,
    512	.init = prp_init,
    513	.remove = prp_remove,
    514};