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: Paul Kocialkowski <paul.kocialkowski@bootlin.com> Acked-by: Maxime Ripard <mripard@kernel.org> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
72 lines
1.8 KiB
C
72 lines
1.8 KiB
C
// 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;
|
|
}
|