mipi csi源码
dts代码如下
csi_dphy0: csi-dphy@ff4b0000 {
compatible = "rockchip,rv1126-csi-dphy";
...
status = "okay";
...
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_ucam0: endpoint@1 {
reg = <1>;
/* imx291 */
remote-endpoint = <&ucam_out2>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_csi2_input>;
};
};
};
};
其实这里可以看出来和imx291直接的联系,imx291注册中dts有这样一句话 remote-endpoint = <&mipi_in_ucam0>;
代码中对remote-enpoint的解析后面也会分析,所以这里也可以知道下一遍文章要分析的subdev是有mipi_csi2_input的port的节点
开始驱动分析
struct mipidphy_priv {
struct device *dev;
...
struct v4l2_async_notifier notifier;
struct v4l2_subdev sd;
...
struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
int num_sensors;
...
bool is_streaming;
...
int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
int (*stream_off)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
};
static int rockchip_mipidphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct v4l2_subdev *sd;
struct mipidphy_priv *priv;
struct regmap *grf;
struct resource *res;
const struct of_device_id *of_id;
const struct dphy_drv_data *drv_data;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
...
priv->stream_on = csi_mipidphy_stream_on;
priv->stream_off = csi_mipidphy_stream_off;
...
sd = &priv->sd;
/*
* 同样的
* sd->ops = &mipidphy_subdev_ops;
* sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
* sd->v4l2_dev = NULL
*/
v4l2_subdev_init(sd, &mipidphy_subdev_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "rockchip-mipi-dphy-rx");
sd->dev = dev;
platform_set_drvdata(pdev, &sd->entity);
ret = rockchip_mipidphy_media_init(priv);
if (ret < 0)
goto destroy_mutex;
return 0;
...
}
static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
{
int ret;
/*
* 下面这几行代码,个人认为加个CONFIG_MEDIA_CONTROLLER控制比较好
* 同样是media,暂时忽略
*/
//#if defined(CONFIG_MEDIA_CONTROLLER)+++++++++++++++++++++++++++++++++++++
priv->pads[MIPI_DPHY_RX_PAD_SOURCE].flags =
MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
priv->pads[MIPI_DPHY_RX_PAD_SINK].flags =
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
ret = media_entity_pads_init(&priv->sd.entity,
MIPI_DPHY_RX_PADS_NUM, priv->pads);
if (ret < 0)
return ret;
//#endif -----------------------------------------------------------------
/*
* 对于v4l2_async_notifier_parse_fwnode_endpoints_by_port
* 看专题分析
*
* 回调函数rockchip_mipidphy_fwnode_parse
* 这里也不再分析,太细节和v4l2关系不大
*/
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
priv->dev, &priv->notifier,
sizeof(struct sensor_async_subdev), 0,
rockchip_mipidphy_fwnode_parse);
if (ret < 0)
return ret;
if (!priv->notifier.num_subdevs)
return -ENODEV; /* no endpoint */
priv->sd.subdev_notifier = &priv->notifier;
/* 注意notifier的ops不为NULL*/
priv->notifier.ops = &rockchip_mipidphy_async_ops;
/*
* 具体的看v4l2_async_subdev_notifier_register 分析
*
* 这个执行后 mipi csi pht的notifier->wait上会挂载asd
* 这个asd就是notifier->subdevs[0]
* 也就是imx291在dts中的节点
* 最后将mipi csi pht的notifier挂载到notifier_list上
*/
ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
if (ret) {
dev_err(priv->dev,
"failed to register async notifier : %dn", ret);
v4l2_async_notifier_cleanup(&priv->notifier);
return ret;
}
/*
* 这里会有v4l2_dev = NULL不执行
* 只是将subdev挂载到subdev_list
*/
return v4l2_async_register_subdev(&priv->sd);
}
这样mipi-csi-phy就注册完了
结合之前的imx291注册
可以知道
notifier_list上有2个notifier,分别是imx291的和mipi-csi-phy的
subdev_list上有2个subdev,分别是imx291的和mipi-csi-phy的
其中mipi-csi-phy的notifier->subdevs[0]中记录了imx291 dts节点的信息
这样就关联起来了
imx291 --> mipi csi phy
mipi csi源码
dts代码如下
csi_dphy0: csi-dphy@ff4b0000 {
compatible = "rockchip,rv1126-csi-dphy";
...
status = "okay";
...
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_ucam0: endpoint@1 {
reg = <1>;
/* imx291 */
remote-endpoint = <&ucam_out2>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_csi2_input>;
};
};
};
};
其实这里可以看出来和imx291直接的联系,imx291注册中dts有这样一句话 remote-endpoint = <&mipi_in_ucam0>;
代码中对remote-enpoint的解析后面也会分析,所以这里也可以知道下一遍文章要分析的subdev是有mipi_csi2_input的port的节点
开始驱动分析
struct mipidphy_priv {
struct device *dev;
...
struct v4l2_async_notifier notifier;
struct v4l2_subdev sd;
...
struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
int num_sensors;
...
bool is_streaming;
...
int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
int (*stream_off)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
};
static int rockchip_mipidphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct v4l2_subdev *sd;
struct mipidphy_priv *priv;
struct regmap *grf;
struct resource *res;
const struct of_device_id *of_id;
const struct dphy_drv_data *drv_data;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
...
priv->stream_on = csi_mipidphy_stream_on;
priv->stream_off = csi_mipidphy_stream_off;
...
sd = &priv->sd;
/*
* 同样的
* sd->ops = &mipidphy_subdev_ops;
* sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
* sd->v4l2_dev = NULL
*/
v4l2_subdev_init(sd, &mipidphy_subdev_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "rockchip-mipi-dphy-rx");
sd->dev = dev;
platform_set_drvdata(pdev, &sd->entity);
ret = rockchip_mipidphy_media_init(priv);
if (ret < 0)
goto destroy_mutex;
return 0;
...
}
static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
{
int ret;
/*
* 下面这几行代码,个人认为加个CONFIG_MEDIA_CONTROLLER控制比较好
* 同样是media,暂时忽略
*/
//#if defined(CONFIG_MEDIA_CONTROLLER)+++++++++++++++++++++++++++++++++++++
priv->pads[MIPI_DPHY_RX_PAD_SOURCE].flags =
MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
priv->pads[MIPI_DPHY_RX_PAD_SINK].flags =
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
ret = media_entity_pads_init(&priv->sd.entity,
MIPI_DPHY_RX_PADS_NUM, priv->pads);
if (ret < 0)
return ret;
//#endif -----------------------------------------------------------------
/*
* 对于v4l2_async_notifier_parse_fwnode_endpoints_by_port
* 看专题分析
*
* 回调函数rockchip_mipidphy_fwnode_parse
* 这里也不再分析,太细节和v4l2关系不大
*/
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
priv->dev, &priv->notifier,
sizeof(struct sensor_async_subdev), 0,
rockchip_mipidphy_fwnode_parse);
if (ret < 0)
return ret;
if (!priv->notifier.num_subdevs)
return -ENODEV; /* no endpoint */
priv->sd.subdev_notifier = &priv->notifier;
/* 注意notifier的ops不为NULL*/
priv->notifier.ops = &rockchip_mipidphy_async_ops;
/*
* 具体的看v4l2_async_subdev_notifier_register 分析
*
* 这个执行后 mipi csi pht的notifier->wait上会挂载asd
* 这个asd就是notifier->subdevs[0]
* 也就是imx291在dts中的节点
* 最后将mipi csi pht的notifier挂载到notifier_list上
*/
ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
if (ret) {
dev_err(priv->dev,
"failed to register async notifier : %dn", ret);
v4l2_async_notifier_cleanup(&priv->notifier);
return ret;
}
/*
* 这里会有v4l2_dev = NULL不执行
* 只是将subdev挂载到subdev_list
*/
return v4l2_async_register_subdev(&priv->sd);
}
这样mipi-csi-phy就注册完了
结合之前的imx291注册
可以知道
notifier_list上有2个notifier,分别是imx291的和mipi-csi-phy的
subdev_list上有2个subdev,分别是imx291的和mipi-csi-phy的
其中mipi-csi-phy的notifier->subdevs[0]中记录了imx291 dts节点的信息
这样就关联起来了
imx291 --> mipi csi phy
举报