brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 4512ac3 Raw
533 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Renesas Electronics Corp.4 *5 * Driver for Renesas R-Car ISP Channel Selector6 *7 * The ISP hardware is capable of more than just channel selection, features8 * such as demosaicing, white balance control and color space conversion are9 * also possible. These more advanced features are not supported by the driver10 * due to lack of documentation.11 */12 13#include <linux/module.h>14#include <linux/mutex.h>15#include <linux/of.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				0x000824#define ISPINPUTSEL0_SEL_CSI0				BIT(31)25 26#define ISPSTART_REG					0x001427#define ISPSTART_START					0xffff28#define ISPSTART_STOP					0x000029 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 = 0x1559	}, {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_state_get_format(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	else305		format->format = *v4l2_subdev_state_get_format(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 links325 */326 327static int risp_notify_bound(struct v4l2_async_notifier *notifier,328			     struct v4l2_subdev *subdev,329			     struct v4l2_async_connection *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_connection *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_connection *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_subdev_nf_init(&isp->notifier, &isp->subdev);396	isp->notifier.ops = &risp_notify_ops;397 398	asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode,399				       struct v4l2_async_connection);400	fwnode_handle_put(fwnode);401	if (IS_ERR(asd))402		return PTR_ERR(asd);403 404	ret = v4l2_async_nf_register(&isp->notifier);405	if (ret)406		v4l2_async_nf_cleanup(&isp->notifier);407 408	return ret;409}410 411/* -----------------------------------------------------------------------------412 * Platform Device Driver413 */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	isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);423	if (IS_ERR(isp->base))424		return PTR_ERR(isp->base);425 426	isp->rstc = devm_reset_control_get(&pdev->dev, NULL);427 428	return PTR_ERR_OR_ZERO(isp->rstc);429}430 431static const struct of_device_id risp_of_id_table[] = {432	{ .compatible = "renesas,r8a779a0-isp" },433	{ .compatible = "renesas,r8a779g0-isp" },434	{ /* sentinel */ },435};436MODULE_DEVICE_TABLE(of, risp_of_id_table);437 438static int risp_probe(struct platform_device *pdev)439{440	struct rcar_isp *isp;441	unsigned int i;442	int ret;443 444	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);445	if (!isp)446		return -ENOMEM;447 448	isp->dev = &pdev->dev;449 450	mutex_init(&isp->lock);451 452	ret = risp_probe_resources(isp, pdev);453	if (ret) {454		dev_err(isp->dev, "Failed to get resources\n");455		goto error_mutex;456	}457 458	platform_set_drvdata(pdev, isp);459 460	pm_runtime_enable(&pdev->dev);461 462	ret = risp_parse_dt(isp);463	if (ret)464		goto error_pm;465 466	isp->subdev.owner = THIS_MODULE;467	isp->subdev.dev = &pdev->dev;468	v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops);469	v4l2_set_subdevdata(&isp->subdev, &pdev->dev);470	snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s",471		 KBUILD_MODNAME, dev_name(&pdev->dev));472	isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;473 474	isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX;475	isp->subdev.entity.ops = &risp_entity_ops;476 477	isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK;478	for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++)479		isp->pads[i].flags = MEDIA_PAD_FL_SOURCE;480 481	ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS,482				     isp->pads);483	if (ret)484		goto error_notifier;485 486	ret = v4l2_async_register_subdev(&isp->subdev);487	if (ret < 0)488		goto error_notifier;489 490	dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);491 492	return 0;493error_notifier:494	v4l2_async_nf_unregister(&isp->notifier);495	v4l2_async_nf_cleanup(&isp->notifier);496error_pm:497	pm_runtime_disable(&pdev->dev);498error_mutex:499	mutex_destroy(&isp->lock);500 501	return ret;502}503 504static void risp_remove(struct platform_device *pdev)505{506	struct rcar_isp *isp = platform_get_drvdata(pdev);507 508	v4l2_async_nf_unregister(&isp->notifier);509	v4l2_async_nf_cleanup(&isp->notifier);510 511	v4l2_async_unregister_subdev(&isp->subdev);512 513	pm_runtime_disable(&pdev->dev);514 515	mutex_destroy(&isp->lock);516}517 518static struct platform_driver rcar_isp_driver = {519	.driver = {520		.name = "rcar-isp",521		.suppress_bind_attrs = true,522		.of_match_table = risp_of_id_table,523	},524	.probe = risp_probe,525	.remove_new = risp_remove,526};527 528module_platform_driver(rcar_isp_driver);529 530MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");531MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver");532MODULE_LICENSE("GPL");533