Commit 576d196c authored by Paul Kocialkowski's avatar Paul Kocialkowski Committed by Mauro Carvalho Chehab
Browse files

media: sunxi: Add support for the A83T MIPI CSI-2 controller



The A83T supports MIPI CSI-2 with a composite controller, covering
both the protocol logic and the D-PHY implementation. This controller
seems to be found on the A83T only and probably was abandoned since.

This implementation splits the protocol and D-PHY registers and
uses the PHY framework internally. The D-PHY is not registered as a
standalone PHY driver since it cannot be used with any other
controller.

There are a few notable points about the controller:
- The initialisation sequence involes writing specific magic init
  values that do not seem to make any particular sense given the
  concerned register fields;
- Interrupts appear to be hitting regardless of the interrupt mask
  registers, which can cause a serious flood when transmission errors
  occur.

Only 8-bit and 10-bit Bayer formats are currently supported.
While up to 4 internal channels to the CSI controller exist, only one
is currently supported by this implementation.

This work is based on the first version of the driver submitted by
Kévin L'hôpital, which was adapted to mainline from the Allwinner BSP.
This version integrates MIPI CSI-2 support as a standalone V4L2 subdev
instead of merging it in the sun6i-csi driver.

It was tested on a Banana Pi M3 board with an OV8865 sensor in a 4-lane
configuration.

Signed-off-by: default avatarPaul Kocialkowski <paul.kocialkowski@bootlin.com>
Acked-by: default avatarMaxime Ripard <mripard@kernel.org>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent e4afdad6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,5 +5,6 @@ comment "Sunxi media platform drivers"
source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig"
source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig"
source "drivers/media/platform/sunxi/sun8i-di/Kconfig"
source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -3,5 +3,6 @@
obj-y		+= sun4i-csi/
obj-y		+= sun6i-csi/
obj-y		+= sun6i-mipi-csi2/
obj-y		+= sun8i-a83t-mipi-csi2/
obj-y		+= sun8i-di/
obj-y		+= sun8i-rotate/
+12 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_SUN8I_A83T_MIPI_CSI2
	tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver"
	depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV
	depends on ARCH_SUNXI || COMPILE_TEST
	depends on PM && COMMON_CLK
	select MEDIA_CONTROLLER
	select VIDEO_V4L2_SUBDEV_API
	select V4L2_FWNODE
	select REGMAP_MMIO
	help
	   Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY.
+4 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
sun8i-a83t-mipi-csi2-y += sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o

obj-$(CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2) += sun8i-a83t-mipi-csi2.o
+72 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2020-2022 Bootlin
 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
 */

#include <linux/phy/phy.h>
#include <linux/regmap.h>

#include "sun8i_a83t_dphy.h"
#include "sun8i_a83t_mipi_csi2.h"

static int sun8i_a83t_dphy_configure(struct phy *dphy,
				     union phy_configure_opts *opts)
{
	return phy_mipi_dphy_config_validate(&opts->mipi_dphy);
}

static int sun8i_a83t_dphy_power_on(struct phy *dphy)
{
	struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy);
	struct regmap *regmap = csi2_dev->regmap;

	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG,
		     SUN8I_A83T_DPHY_CTRL_RESET_N |
		     SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N);

	regmap_write(regmap, SUN8I_A83T_DPHY_ANA0_REG,
		     SUN8I_A83T_DPHY_ANA0_REXT_EN |
		     SUN8I_A83T_DPHY_ANA0_RINT(2) |
		     SUN8I_A83T_DPHY_ANA0_SNK(2));

	return 0;
};

static int sun8i_a83t_dphy_power_off(struct phy *dphy)
{
	struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy);
	struct regmap *regmap = csi2_dev->regmap;

	regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0);

	return 0;
};

static const struct phy_ops sun8i_a83t_dphy_ops = {
	.configure	= sun8i_a83t_dphy_configure,
	.power_on	= sun8i_a83t_dphy_power_on,
	.power_off	= sun8i_a83t_dphy_power_off,
};

int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_device *csi2_dev)
{
	struct device *dev = csi2_dev->dev;
	struct phy_provider *phy_provider;

	csi2_dev->dphy = devm_phy_create(dev, NULL, &sun8i_a83t_dphy_ops);
	if (IS_ERR(csi2_dev->dphy)) {
		dev_err(dev, "failed to create D-PHY\n");
		return PTR_ERR(csi2_dev->dphy);
	}

	phy_set_drvdata(csi2_dev->dphy, csi2_dev);

	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
	if (IS_ERR(phy_provider)) {
		dev_err(dev, "failed to register D-PHY provider\n");
		return PTR_ERR(phy_provider);
	}

	return 0;
}
Loading