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

rcar-isp.c (12704B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2021 Renesas Electronics Corp.
      4 *
      5 * Driver for Renesas R-Car ISP Channel Selector
      6 *
      7 * The ISP hardware is capable of more than just channel selection, features
      8 * such as demosaicing, white balance control and color space conversion are
      9 * also possible. These more advanced features are not supported by the driver
     10 * due to lack of documentation.
     11 */
     12
     13#include <linux/module.h>
     14#include <linux/mutex.h>
     15#include <linux/of_device.h>
     16#include <linux/platform_device.h>
     17#include <linux/pm_runtime.h>
     18#include <linux/reset.h>
     19
     20#include <media/mipi-csi2.h>
     21#include <media/v4l2-subdev.h>
     22
     23#define ISPINPUTSEL0_REG				0x0008
     24#define ISPINPUTSEL0_SEL_CSI0				BIT(31)
     25
     26#define ISPSTART_REG					0x0014
     27#define ISPSTART_START					0xffff
     28#define ISPSTART_STOP					0x0000
     29
     30#define ISPPROCMODE_DT_REG(n)				(0x1100 + (0x4 * (n)))
     31#define ISPPROCMODE_DT_PROC_MODE_VC3(pm)		(((pm) & 0x3f) << 24)
     32#define ISPPROCMODE_DT_PROC_MODE_VC2(pm)		(((pm) & 0x3f) << 16)
     33#define ISPPROCMODE_DT_PROC_MODE_VC1(pm)		(((pm) & 0x3f) << 8)
     34#define ISPPROCMODE_DT_PROC_MODE_VC0(pm)		((pm) & 0x3f)
     35
     36#define ISPCS_FILTER_ID_CH_REG(n)			(0x3000 + (0x0100 * (n)))
     37
     38#define ISPCS_DT_CODE03_CH_REG(n)			(0x3008 + (0x100 * (n)))
     39#define ISPCS_DT_CODE03_EN3				BIT(31)
     40#define ISPCS_DT_CODE03_DT3(dt)				(((dt) & 0x3f) << 24)
     41#define ISPCS_DT_CODE03_EN2				BIT(23)
     42#define ISPCS_DT_CODE03_DT2(dt)				(((dt) & 0x3f) << 16)
     43#define ISPCS_DT_CODE03_EN1				BIT(15)
     44#define ISPCS_DT_CODE03_DT1(dt)				(((dt) & 0x3f) << 8)
     45#define ISPCS_DT_CODE03_EN0				BIT(7)
     46#define ISPCS_DT_CODE03_DT0(dt)				((dt) & 0x3f)
     47
     48struct rcar_isp_format {
     49	u32 code;
     50	unsigned int datatype;
     51	unsigned int procmode;
     52};
     53
     54static const struct rcar_isp_format rcar_isp_formats[] = {
     55	{
     56		.code = MEDIA_BUS_FMT_RGB888_1X24,
     57		.datatype = MIPI_CSI2_DT_RGB888,
     58		.procmode = 0x15
     59	}, {
     60		.code = MEDIA_BUS_FMT_Y10_1X10,
     61		.datatype = MIPI_CSI2_DT_RAW10,
     62		.procmode = 0x10,
     63	}, {
     64		.code = MEDIA_BUS_FMT_UYVY8_1X16,
     65		.datatype = MIPI_CSI2_DT_YUV422_8B,
     66		.procmode = 0x0c,
     67	}, {
     68		.code = MEDIA_BUS_FMT_YUYV8_1X16,
     69		.datatype = MIPI_CSI2_DT_YUV422_8B,
     70		.procmode = 0x0c,
     71	}, {
     72		.code = MEDIA_BUS_FMT_UYVY8_2X8,
     73		.datatype = MIPI_CSI2_DT_YUV422_8B,
     74		.procmode = 0x0c,
     75	}, {
     76		.code = MEDIA_BUS_FMT_YUYV10_2X10,
     77		.datatype = MIPI_CSI2_DT_YUV422_8B,
     78		.procmode = 0x0c,
     79	},
     80};
     81
     82static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code)
     83{
     84	unsigned int i;
     85
     86	for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) {
     87		if (rcar_isp_formats[i].code == code)
     88			return &rcar_isp_formats[i];
     89	}
     90
     91	return NULL;
     92}
     93
     94enum rcar_isp_input {
     95	RISP_CSI_INPUT0,
     96	RISP_CSI_INPUT1,
     97};
     98
     99enum rcar_isp_pads {
    100	RCAR_ISP_SINK,
    101	RCAR_ISP_PORT0,
    102	RCAR_ISP_PORT1,
    103	RCAR_ISP_PORT2,
    104	RCAR_ISP_PORT3,
    105	RCAR_ISP_PORT4,
    106	RCAR_ISP_PORT5,
    107	RCAR_ISP_PORT6,
    108	RCAR_ISP_PORT7,
    109	RCAR_ISP_NUM_PADS,
    110};
    111
    112struct rcar_isp {
    113	struct device *dev;
    114	void __iomem *base;
    115	struct reset_control *rstc;
    116
    117	enum rcar_isp_input csi_input;
    118
    119	struct v4l2_subdev subdev;
    120	struct media_pad pads[RCAR_ISP_NUM_PADS];
    121
    122	struct v4l2_async_notifier notifier;
    123	struct v4l2_subdev *remote;
    124
    125	struct mutex lock; /* Protects mf and stream_count. */
    126	struct v4l2_mbus_framefmt mf;
    127	int stream_count;
    128};
    129
    130static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd)
    131{
    132	return container_of(sd, struct rcar_isp, subdev);
    133}
    134
    135static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n)
    136{
    137	return container_of(n, struct rcar_isp, notifier);
    138}
    139
    140static void risp_write(struct rcar_isp *isp, u32 offset, u32 value)
    141{
    142	iowrite32(value, isp->base + offset);
    143}
    144
    145static u32 risp_read(struct rcar_isp *isp, u32 offset)
    146{
    147	return ioread32(isp->base + offset);
    148}
    149
    150static int risp_power_on(struct rcar_isp *isp)
    151{
    152	int ret;
    153
    154	ret = pm_runtime_resume_and_get(isp->dev);
    155	if (ret < 0)
    156		return ret;
    157
    158	ret = reset_control_deassert(isp->rstc);
    159	if (ret < 0) {
    160		pm_runtime_put(isp->dev);
    161		return ret;
    162	}
    163
    164	return 0;
    165}
    166
    167static void risp_power_off(struct rcar_isp *isp)
    168{
    169	reset_control_assert(isp->rstc);
    170	pm_runtime_put(isp->dev);
    171}
    172
    173static int risp_start(struct rcar_isp *isp)
    174{
    175	const struct rcar_isp_format *format;
    176	unsigned int vc;
    177	u32 sel_csi = 0;
    178	int ret;
    179
    180	format = risp_code_to_fmt(isp->mf.code);
    181	if (!format) {
    182		dev_err(isp->dev, "Unsupported bus format\n");
    183		return -EINVAL;
    184	}
    185
    186	ret = risp_power_on(isp);
    187	if (ret) {
    188		dev_err(isp->dev, "Failed to power on ISP\n");
    189		return ret;
    190	}
    191
    192	/* Select CSI-2 input source. */
    193	if (isp->csi_input == RISP_CSI_INPUT1)
    194		sel_csi = ISPINPUTSEL0_SEL_CSI0;
    195
    196	risp_write(isp, ISPINPUTSEL0_REG,
    197		   risp_read(isp, ISPINPUTSEL0_REG) | sel_csi);
    198
    199	/* Configure Channel Selector. */
    200	for (vc = 0; vc < 4; vc++) {
    201		u8 ch = vc + 4;
    202		u8 dt = format->datatype;
    203
    204		risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
    205		risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch),
    206			   ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
    207			   ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
    208			   ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
    209			   ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
    210	}
    211
    212	/* Setup processing method. */
    213	risp_write(isp, ISPPROCMODE_DT_REG(format->datatype),
    214		   ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
    215		   ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
    216		   ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
    217		   ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
    218
    219	/* Start ISP. */
    220	risp_write(isp, ISPSTART_REG, ISPSTART_START);
    221
    222	ret = v4l2_subdev_call(isp->remote, video, s_stream, 1);
    223	if (ret)
    224		risp_power_off(isp);
    225
    226	return ret;
    227}
    228
    229static void risp_stop(struct rcar_isp *isp)
    230{
    231	v4l2_subdev_call(isp->remote, video, s_stream, 0);
    232
    233	/* Stop ISP. */
    234	risp_write(isp, ISPSTART_REG, ISPSTART_STOP);
    235
    236	risp_power_off(isp);
    237}
    238
    239static int risp_s_stream(struct v4l2_subdev *sd, int enable)
    240{
    241	struct rcar_isp *isp = sd_to_isp(sd);
    242	int ret = 0;
    243
    244	mutex_lock(&isp->lock);
    245
    246	if (!isp->remote) {
    247		ret = -ENODEV;
    248		goto out;
    249	}
    250
    251	if (enable && isp->stream_count == 0) {
    252		ret = risp_start(isp);
    253		if (ret)
    254			goto out;
    255	} else if (!enable && isp->stream_count == 1) {
    256		risp_stop(isp);
    257	}
    258
    259	isp->stream_count += enable ? 1 : -1;
    260out:
    261	mutex_unlock(&isp->lock);
    262
    263	return ret;
    264}
    265
    266static const struct v4l2_subdev_video_ops risp_video_ops = {
    267	.s_stream = risp_s_stream,
    268};
    269
    270static int risp_set_pad_format(struct v4l2_subdev *sd,
    271			       struct v4l2_subdev_state *sd_state,
    272			       struct v4l2_subdev_format *format)
    273{
    274	struct rcar_isp *isp = sd_to_isp(sd);
    275	struct v4l2_mbus_framefmt *framefmt;
    276
    277	mutex_lock(&isp->lock);
    278
    279	if (!risp_code_to_fmt(format->format.code))
    280		format->format.code = rcar_isp_formats[0].code;
    281
    282	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
    283		isp->mf = format->format;
    284	} else {
    285		framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0);
    286		*framefmt = format->format;
    287	}
    288
    289	mutex_unlock(&isp->lock);
    290
    291	return 0;
    292}
    293
    294static int risp_get_pad_format(struct v4l2_subdev *sd,
    295			       struct v4l2_subdev_state *sd_state,
    296			       struct v4l2_subdev_format *format)
    297{
    298	struct rcar_isp *isp = sd_to_isp(sd);
    299
    300	mutex_lock(&isp->lock);
    301
    302	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
    303		format->format = isp->mf;
    304	else
    305		format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0);
    306
    307	mutex_unlock(&isp->lock);
    308
    309	return 0;
    310}
    311
    312static const struct v4l2_subdev_pad_ops risp_pad_ops = {
    313	.set_fmt = risp_set_pad_format,
    314	.get_fmt = risp_get_pad_format,
    315	.link_validate = v4l2_subdev_link_validate_default,
    316};
    317
    318static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {
    319	.video	= &risp_video_ops,
    320	.pad	= &risp_pad_ops,
    321};
    322
    323/* -----------------------------------------------------------------------------
    324 * Async handling and registration of subdevices and links
    325 */
    326
    327static int risp_notify_bound(struct v4l2_async_notifier *notifier,
    328			     struct v4l2_subdev *subdev,
    329			     struct v4l2_async_subdev *asd)
    330{
    331	struct rcar_isp *isp = notifier_to_isp(notifier);
    332	int pad;
    333
    334	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
    335					  MEDIA_PAD_FL_SOURCE);
    336	if (pad < 0) {
    337		dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name);
    338		return pad;
    339	}
    340
    341	isp->remote = subdev;
    342
    343	dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad);
    344
    345	return media_create_pad_link(&subdev->entity, pad,
    346				     &isp->subdev.entity, 0,
    347				     MEDIA_LNK_FL_ENABLED |
    348				     MEDIA_LNK_FL_IMMUTABLE);
    349}
    350
    351static void risp_notify_unbind(struct v4l2_async_notifier *notifier,
    352			       struct v4l2_subdev *subdev,
    353			       struct v4l2_async_subdev *asd)
    354{
    355	struct rcar_isp *isp = notifier_to_isp(notifier);
    356
    357	isp->remote = NULL;
    358
    359	dev_dbg(isp->dev, "Unbind %s\n", subdev->name);
    360}
    361
    362static const struct v4l2_async_notifier_operations risp_notify_ops = {
    363	.bound = risp_notify_bound,
    364	.unbind = risp_notify_unbind,
    365};
    366
    367static int risp_parse_dt(struct rcar_isp *isp)
    368{
    369	struct v4l2_async_subdev *asd;
    370	struct fwnode_handle *fwnode;
    371	struct fwnode_handle *ep;
    372	unsigned int id;
    373	int ret;
    374
    375	for (id = 0; id < 2; id++) {
    376		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev),
    377						     0, id, 0);
    378		if (ep)
    379			break;
    380	}
    381
    382	if (!ep) {
    383		dev_err(isp->dev, "Not connected to subdevice\n");
    384		return -EINVAL;
    385	}
    386
    387	if (id == 1)
    388		isp->csi_input = RISP_CSI_INPUT1;
    389
    390	fwnode = fwnode_graph_get_remote_endpoint(ep);
    391	fwnode_handle_put(ep);
    392
    393	dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode));
    394
    395	v4l2_async_nf_init(&isp->notifier);
    396	isp->notifier.ops = &risp_notify_ops;
    397
    398	asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode,
    399				       struct v4l2_async_subdev);
    400	fwnode_handle_put(fwnode);
    401	if (IS_ERR(asd))
    402		return PTR_ERR(asd);
    403
    404	ret = v4l2_async_subdev_nf_register(&isp->subdev, &isp->notifier);
    405	if (ret)
    406		v4l2_async_nf_cleanup(&isp->notifier);
    407
    408	return ret;
    409}
    410
    411/* -----------------------------------------------------------------------------
    412 * Platform Device Driver
    413 */
    414
    415static const struct media_entity_operations risp_entity_ops = {
    416	.link_validate = v4l2_subdev_link_validate,
    417};
    418
    419static int risp_probe_resources(struct rcar_isp *isp,
    420				struct platform_device *pdev)
    421{
    422	struct resource *res;
    423
    424	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    425	isp->base = devm_ioremap_resource(&pdev->dev, res);
    426	if (IS_ERR(isp->base))
    427		return PTR_ERR(isp->base);
    428
    429	isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
    430
    431	return PTR_ERR_OR_ZERO(isp->rstc);
    432}
    433
    434static const struct of_device_id risp_of_id_table[] = {
    435	{ .compatible = "renesas,r8a779a0-isp" },
    436	{ /* sentinel */ },
    437};
    438MODULE_DEVICE_TABLE(of, risp_of_id_table);
    439
    440static int risp_probe(struct platform_device *pdev)
    441{
    442	struct rcar_isp *isp;
    443	unsigned int i;
    444	int ret;
    445
    446	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
    447	if (!isp)
    448		return -ENOMEM;
    449
    450	isp->dev = &pdev->dev;
    451
    452	mutex_init(&isp->lock);
    453
    454	ret = risp_probe_resources(isp, pdev);
    455	if (ret) {
    456		dev_err(isp->dev, "Failed to get resources\n");
    457		goto error_mutex;
    458	}
    459
    460	platform_set_drvdata(pdev, isp);
    461
    462	pm_runtime_enable(&pdev->dev);
    463
    464	ret = risp_parse_dt(isp);
    465	if (ret)
    466		goto error_pm;
    467
    468	isp->subdev.owner = THIS_MODULE;
    469	isp->subdev.dev = &pdev->dev;
    470	v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops);
    471	v4l2_set_subdevdata(&isp->subdev, &pdev->dev);
    472	snprintf(isp->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s %s",
    473		 KBUILD_MODNAME, dev_name(&pdev->dev));
    474	isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
    475
    476	isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
    477	isp->subdev.entity.ops = &risp_entity_ops;
    478
    479	isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK;
    480	for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++)
    481		isp->pads[i].flags = MEDIA_PAD_FL_SOURCE;
    482
    483	ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS,
    484				     isp->pads);
    485	if (ret)
    486		goto error_notifier;
    487
    488	ret = v4l2_async_register_subdev(&isp->subdev);
    489	if (ret < 0)
    490		goto error_notifier;
    491
    492	dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
    493
    494	return 0;
    495error_notifier:
    496	v4l2_async_nf_unregister(&isp->notifier);
    497	v4l2_async_nf_cleanup(&isp->notifier);
    498error_pm:
    499	pm_runtime_disable(&pdev->dev);
    500error_mutex:
    501	mutex_destroy(&isp->lock);
    502
    503	return ret;
    504}
    505
    506static int risp_remove(struct platform_device *pdev)
    507{
    508	struct rcar_isp *isp = platform_get_drvdata(pdev);
    509
    510	v4l2_async_nf_unregister(&isp->notifier);
    511	v4l2_async_nf_cleanup(&isp->notifier);
    512
    513	v4l2_async_unregister_subdev(&isp->subdev);
    514
    515	pm_runtime_disable(&pdev->dev);
    516
    517	mutex_destroy(&isp->lock);
    518
    519	return 0;
    520}
    521
    522static struct platform_driver rcar_isp_driver = {
    523	.driver = {
    524		.name = "rcar-isp",
    525		.of_match_table = risp_of_id_table,
    526	},
    527	.probe = risp_probe,
    528	.remove = risp_remove,
    529};
    530
    531module_platform_driver(rcar_isp_driver);
    532
    533MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
    534MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver");
    535MODULE_LICENSE("GPL");