Commit 8082e2f4 authored by Paul Elder's avatar Paul Elder Committed by Mauro Carvalho Chehab
Browse files

media: rkisp1: Split CSI handling to separate file



Not all ISP instances include a MIPI CSI-2 receiver. To prepare for
making it optional, move code related to the CSI-2 receiver to a
separate file.

Signed-off-by: default avatarPaul Elder <paul.elder@ideasonboard.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: default avatarDafna Hirschfeld <dafna@fastmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent b0b8ab68
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

rockchip-isp1-y := rkisp1-capture.o \
		   rkisp1-common.o \
		   rkisp1-csi.o \
		   rkisp1-dev.o \
		   rkisp1-isp.o \
		   rkisp1-resizer.o \
+14 −3
Original line number Diff line number Diff line
@@ -123,7 +123,6 @@ struct rkisp1_info {
 * @mbus_flags:		media bus (V4L2_MBUS_*) flags
 * @sd:			a pointer to v4l2_subdev struct of the sensor
 * @pixel_rate_ctrl:	pixel rate of the sensor, used to initialize the phy
 * @dphy:		a pointer to the phy
 */
struct rkisp1_sensor_async {
	struct v4l2_async_subdev asd;
@@ -134,7 +133,19 @@ struct rkisp1_sensor_async {
	unsigned int mbus_flags;
	struct v4l2_subdev *sd;
	struct v4l2_ctrl *pixel_rate_ctrl;
};

/*
 * struct rkisp1_csi - CSI receiver subdev
 *
 * @rkisp1: pointer to the rkisp1 device
 * @dphy: a pointer to the phy
 * @is_dphy_errctrl_disabled: if dphy errctrl is disabled (avoid endless interrupt)
 */
struct rkisp1_csi {
	struct rkisp1_device *rkisp1;
	struct phy *dphy;
	bool is_dphy_errctrl_disabled;
};

/*
@@ -147,7 +158,6 @@ struct rkisp1_sensor_async {
 * @sink_fmt:			input format
 * @src_fmt:			output format
 * @ops_lock:			ops serialization
 * @is_dphy_errctrl_disabled:	if dphy errctrl is disabled (avoid endless interrupt)
 * @frame_sequence:		used to synchronize frame_id between video devices.
 */
struct rkisp1_isp {
@@ -157,7 +167,6 @@ struct rkisp1_isp {
	const struct rkisp1_mbus_info *sink_fmt;
	const struct rkisp1_mbus_info *src_fmt;
	struct mutex ops_lock; /* serialize the subdevice ops */
	bool is_dphy_errctrl_disabled;
	__u32 frame_sequence;
};

@@ -402,6 +411,7 @@ struct rkisp1_debug {
 * @media_dev:	   media_device variable
 * @notifier:	   a notifier to register on the v4l2-async API to be notified on the sensor
 * @active_sensor: sensor in-use, set when streaming on
 * @csi:	   internal CSI-2 receiver
 * @isp:	   ISP sub-device
 * @resizer_devs:  resizer sub-devices
 * @capture_devs:  capture devices
@@ -421,6 +431,7 @@ struct rkisp1_device {
	struct media_device media_dev;
	struct v4l2_async_notifier notifier;
	struct rkisp1_sensor_async *active_sensor;
	struct rkisp1_csi csi;
	struct rkisp1_isp isp;
	struct rkisp1_resizer resizer_devs[2];
	struct rkisp1_capture capture_devs[2];
+193 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Rockchip ISP1 Driver - CSI-2 Receiver
 *
 * Copyright (C) 2019 Collabora, Ltd.
 * Copyright (C) 2022 Ideas on Board
 *
 * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
 * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
 */

#include <linux/device.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>

#include <media/v4l2-ctrls.h>

#include "rkisp1-common.h"
#include "rkisp1-csi.h"

int rkisp1_config_mipi(struct rkisp1_csi *csi)
{
	struct rkisp1_device *rkisp1 = csi->rkisp1;
	const struct rkisp1_mbus_info *sink_fmt = rkisp1->isp.sink_fmt;
	unsigned int lanes = rkisp1->active_sensor->lanes;
	u32 mipi_ctrl;

	if (lanes < 1 || lanes > 4)
		return -EINVAL;

	mipi_ctrl = RKISP1_CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
		    RKISP1_CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
		    RKISP1_CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
		    RKISP1_CIF_MIPI_CTRL_CLOCKLANE_ENA;

	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL, mipi_ctrl);

	/* V12 could also use a newer csi2-host, but we don't want that yet */
	if (rkisp1->info->isp_ver == RKISP1_V12)
		rkisp1_write(rkisp1, RKISP1_CIF_ISP_CSI0_CTRL0, 0);

	/* Configure Data Type and Virtual Channel */
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL,
		     RKISP1_CIF_MIPI_DATA_SEL_DT(sink_fmt->mipi_dt) |
		     RKISP1_CIF_MIPI_DATA_SEL_VC(0));

	/* Clear MIPI interrupts */
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);

	/*
	 * Disable RKISP1_CIF_MIPI_ERR_DPHY interrupt here temporary for
	 * isp bus may be dead when switch isp.
	 */
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
		     RKISP1_CIF_MIPI_FRAME_END | RKISP1_CIF_MIPI_ERR_CSI |
		     RKISP1_CIF_MIPI_ERR_DPHY |
		     RKISP1_CIF_MIPI_SYNC_FIFO_OVFLW(0x03) |
		     RKISP1_CIF_MIPI_ADD_DATA_OVFLW);

	dev_dbg(rkisp1->dev, "\n  MIPI_CTRL 0x%08x\n"
		"  MIPI_IMG_DATA_SEL 0x%08x\n"
		"  MIPI_STATUS 0x%08x\n"
		"  MIPI_IMSC 0x%08x\n",
		rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL),
		rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMG_DATA_SEL),
		rkisp1_read(rkisp1, RKISP1_CIF_MIPI_STATUS),
		rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC));

	return 0;
}

int rkisp1_mipi_csi2_start(struct rkisp1_csi *csi,
			   struct rkisp1_sensor_async *sensor)
{
	struct rkisp1_device *rkisp1 = csi->rkisp1;
	union phy_configure_opts opts;
	struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
	s64 pixel_clock;

	pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl);
	if (!pixel_clock) {
		dev_err(rkisp1->dev, "Invalid pixel rate value\n");
		return -EINVAL;
	}

	phy_mipi_dphy_get_default_config(pixel_clock,
					 rkisp1->isp.sink_fmt->bus_width,
					 sensor->lanes, cfg);
	phy_set_mode(csi->dphy, PHY_MODE_MIPI_DPHY);
	phy_configure(csi->dphy, &opts);
	phy_power_on(csi->dphy);

	return 0;
}

void rkisp1_mipi_csi2_stop(struct rkisp1_csi *csi)
{
	phy_power_off(csi->dphy);
}

void rkisp1_mipi_start(struct rkisp1_csi *csi)
{
	struct rkisp1_device *rkisp1 = csi->rkisp1;
	u32 val;

	val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
		     val | RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA);
}

void rkisp1_mipi_stop(struct rkisp1_csi *csi)
{
	struct rkisp1_device *rkisp1 = csi->rkisp1;
	u32 val;

	/* Mask and clear interrupts. */
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, 0);
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, ~0);

	val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_CTRL);
	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_CTRL,
		     val & (~RKISP1_CIF_MIPI_CTRL_OUTPUT_ENA));
}

irqreturn_t rkisp1_mipi_isr(int irq, void *ctx)
{
	struct device *dev = ctx;
	struct rkisp1_device *rkisp1 = dev_get_drvdata(dev);
	u32 val, status;

	status = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_MIS);
	if (!status)
		return IRQ_NONE;

	rkisp1_write(rkisp1, RKISP1_CIF_MIPI_ICR, status);

	/*
	 * Disable DPHY errctrl interrupt, because this dphy
	 * erctrl signal is asserted until the next changes
	 * of line state. This time is may be too long and cpu
	 * is hold in this interrupt.
	 */
	if (status & RKISP1_CIF_MIPI_ERR_CTRL(0x0f)) {
		val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
		rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC,
			     val & ~RKISP1_CIF_MIPI_ERR_CTRL(0x0f));
		rkisp1->csi.is_dphy_errctrl_disabled = true;
	}

	/*
	 * Enable DPHY errctrl interrupt again, if mipi have receive
	 * the whole frame without any error.
	 */
	if (status == RKISP1_CIF_MIPI_FRAME_END) {
		/*
		 * Enable DPHY errctrl interrupt again, if mipi have receive
		 * the whole frame without any error.
		 */
		if (rkisp1->csi.is_dphy_errctrl_disabled) {
			val = rkisp1_read(rkisp1, RKISP1_CIF_MIPI_IMSC);
			val |= RKISP1_CIF_MIPI_ERR_CTRL(0x0f);
			rkisp1_write(rkisp1, RKISP1_CIF_MIPI_IMSC, val);
			rkisp1->csi.is_dphy_errctrl_disabled = false;
		}
	} else {
		rkisp1->debug.mipi_error++;
	}

	return IRQ_HANDLED;
}

int rkisp1_csi_init(struct rkisp1_device *rkisp1)
{
	struct rkisp1_csi *csi = &rkisp1->csi;

	csi->rkisp1 = rkisp1;

	csi->dphy = devm_phy_get(rkisp1->dev, "dphy");
	if (IS_ERR(csi->dphy))
		return dev_err_probe(rkisp1->dev, PTR_ERR(csi->dphy),
				     "Couldn't get the MIPI D-PHY\n");

	phy_init(csi->dphy);

	return 0;
}

void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1)
{
	struct rkisp1_csi *csi = &rkisp1->csi;

	phy_exit(csi->dphy);
}
+29 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
 * Rockchip ISP1 Driver - CSI-2 Receiver
 *
 * Copyright (C) 2019 Collabora, Ltd.
 * Copyright (C) 2022 Ideas on Board
 *
 * Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
 * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
 */
#ifndef _RKISP1_CSI_H
#define _RKISP1_CSI_H

struct rkisp1_csi;
struct rkisp1_device;
struct rkisp1_sensor_async;

int rkisp1_csi_init(struct rkisp1_device *rkisp1);
void rkisp1_csi_cleanup(struct rkisp1_device *rkisp1);

int rkisp1_config_mipi(struct rkisp1_csi *csi);

int rkisp1_mipi_csi2_start(struct rkisp1_csi *csi,
			   struct rkisp1_sensor_async *sensor);
void rkisp1_mipi_csi2_stop(struct rkisp1_csi *csi);
void rkisp1_mipi_start(struct rkisp1_csi *csi);
void rkisp1_mipi_stop(struct rkisp1_csi *csi);

#endif /* _RKISP1_CSI_H */
+10 −22
Original line number Diff line number Diff line
@@ -15,11 +15,11 @@
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-fwnode.h>

#include "rkisp1-common.h"
#include "rkisp1-csi.h"

/*
 * ISP Details
@@ -128,14 +128,6 @@ static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
	}

	s_asd->sd = sd;
	s_asd->dphy = devm_phy_get(rkisp1->dev, "dphy");
	if (IS_ERR(s_asd->dphy)) {
		if (PTR_ERR(s_asd->dphy) != -EPROBE_DEFER)
			dev_err(rkisp1->dev, "Couldn't get the MIPI D-PHY\n");
		return PTR_ERR(s_asd->dphy);
	}

	phy_init(s_asd->dphy);

	/* Create the link to the sensor. */
	source_pad = media_entity_get_fwnode_pad(&sd->entity, s_asd->source_ep,
@@ -152,16 +144,6 @@ static int rkisp1_subdev_notifier_bound(struct v4l2_async_notifier *notifier,
				     !s_asd->index ? MEDIA_LNK_FL_ENABLED : 0);
}

static void rkisp1_subdev_notifier_unbind(struct v4l2_async_notifier *notifier,
					  struct v4l2_subdev *sd,
					  struct v4l2_async_subdev *asd)
{
	struct rkisp1_sensor_async *s_asd =
		container_of(asd, struct rkisp1_sensor_async, asd);

	phy_exit(s_asd->dphy);
}

static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
	struct rkisp1_device *rkisp1 =
@@ -180,7 +162,6 @@ static void rkisp1_subdev_notifier_destroy(struct v4l2_async_subdev *asd)

static const struct v4l2_async_notifier_operations rkisp1_subdev_notifier_ops = {
	.bound = rkisp1_subdev_notifier_bound,
	.unbind = rkisp1_subdev_notifier_unbind,
	.complete = rkisp1_subdev_notifier_complete,
	.destroy = rkisp1_subdev_notifier_destroy,
};
@@ -540,14 +521,20 @@ static int rkisp1_probe(struct platform_device *pdev)
		goto err_unreg_v4l2_dev;
	}

	ret = rkisp1_entities_register(rkisp1);
	ret = rkisp1_csi_init(rkisp1);
	if (ret)
		goto err_unreg_media_dev;

	ret = rkisp1_entities_register(rkisp1);
	if (ret)
		goto err_cleanup_csi;

	rkisp1_debug_init(rkisp1);

	return 0;

err_cleanup_csi:
	rkisp1_csi_cleanup(rkisp1);
err_unreg_media_dev:
	media_device_unregister(&rkisp1->media_dev);
err_unreg_v4l2_dev:
@@ -565,6 +552,7 @@ static int rkisp1_remove(struct platform_device *pdev)
	v4l2_async_nf_cleanup(&rkisp1->notifier);

	rkisp1_entities_unregister(rkisp1);
	rkisp1_csi_cleanup(rkisp1);
	rkisp1_debug_cleanup(rkisp1);

	media_device_unregister(&rkisp1->media_dev);
Loading