此次调试的是Linux系统的kernel5.10
dts配置
ite_bridge: it6122@6c {
status = "okay";
compatible = "ite,it6122";
reg = <0x6c>;
ite,bypass_mode;
ite,bypass_hsw = <40>;
ite,bypass_vsw = <2>;
//ite,hresync;
ite,dsi-lanes = <4>;
ite,dsi-channel = <0>;
ite,skip-stage = <3>;
ite,hs-settle = <4>;
mipi-inv-mclk;
lvds-dual-mode;
lvds-vesa-mode;
lvds-swing-level=<6>;
//lvds-ssc-enable;
lvds-sdm = <0x3333>;
lvds-sdm-inv = <0x106>;
pinctrl-0 = <&it612x_irq>;
interrupt-parent = <&gpio1>;
interrupts = ;
reset-gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
vcc1v2-gpios = <&gpio2 RK_PA6 GPIO_ACTIVE_HIGH>;
vcc1v8-gpios = <&gpio2 RK_PA7 GPIO_ACTIVE_HIGH>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mipi_in: endpoint {
remote-endpoint = <&dsi_out_panel>;
};
};
port@1 {
reg = <1>;
lvds_out: endpoint {
remote-endpoint = <&lvds_panel>;
};
};
};
};
&dsi0 {
status = "disabled";
//rockchip,lane-rate = <1000>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out_panel: endpoint {
remote-endpoint = <&mipi_in>;
};
};
};
};
lvds_panel0: panel {
status = "okay";
compatible = "auo,g190ean01","simple-panel";
backlight = <&backlight>;
reset-delay-ms = <10>;
enable-delay-ms = <10>;
prepare-delay-ms = <10>;
unprepare-delay-ms = <10>;
disable-delay-ms = <60>;
disp_timings0: display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <132000000>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <15>;
hsync-len = <4>;
hback-porch = <30>;
vfront-porch = <15>;
vsync-len = <2>;
vback-porch = <15>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds_panel: endpoint {
remote-endpoint = <&lvds_out>;
};
};
};
};
流程
现在要添加的是it6122就是drm bridge
在dw-mipi-dsi2-rockchip.c中dw_mipi_dsi2_bind函数
static int dw_mipi_dsi2_bind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi2 *dsi2 = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
struct drm_encoder *encoder = &dsi2->encoder;
struct device_node *of_node = dsi2->dev->of_node;
struct drm_connector *connector = NULL;
enum drm_bridge_attach_flags flags;
int ret;
dsi2->drm_dev = drm_dev;
ret = dw_mipi_dsi2_dual_channel_probe(dsi2);
if (ret)
return ret;
if (dsi2->master)
return 0;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
&dsi2->panel, &dsi2->bridge);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to find panel or bridge: %d\n", ret);
return ret;
}
dw_mipi_dsi2_get_dsc_params_from_sink(dsi2, dsi2->panel, dsi2->bridge);
encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
of_node);
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
if (ret) {
DRM_ERROR("Failed to initialize encoder with drm\n");
return ret;
}
drm_encoder_helper_add(encoder, &dw_mipi_dsi2_encoder_helper_funcs);
if (dsi2->bridge) {
struct list_head *connector_list =
&drm_dev->mode_config.connector_list;
dsi2->bridge->driver_private = &dsi2->host;
dsi2->bridge->encoder = encoder;
flags = dsi2->bridge->ops & DRM_BRIDGE_OP_MODES ?
DRM_BRIDGE_ATTACH_NO_CONNECTOR : 0;
ret = drm_bridge_attach(encoder, dsi2->bridge, NULL, flags);
if (ret) {
DRM_DEV_ERROR(dev,
"Failed to attach bridge: %d\n", ret);
goto encoder_cleanup;
}
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
list_for_each_entry(connector, connector_list, head)
if (drm_connector_has_possible_encoder(connector,
encoder))
break;
}
if (dsi2->panel || (dsi2->bridge && (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))) {
ret = dw_mipi_dsi2_connector_init(dsi2);
if (ret)
goto encoder_cleanup;
connector = &dsi2->connector;
}
if (connector) {
ret = dw_mipi_dsi2_register_sub_dev(dsi2, connector);
if (ret)
goto encoder_cleanup;
}
pm_runtime_enable(dsi2->dev);
if (dsi2->slave)
pm_runtime_enable(dsi2->slave->dev);
return 0;
encoder_cleanup:
encoder->funcs->destroy(encoder);
return ret;
}
关键函数drm_of_find_panel_or_bridge通过dts中dsi0节点的endpoint找到连接的bridge或panel
int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint,
struct drm_panel **panel,
struct drm_bridge **bridge)
{
int ret = -EPROBE_DEFER;
struct device_node *remote;
if (!panel && !bridge)
return -EINVAL;
if (panel)
*panel = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
* node isn't found and the absence of the port is a legit case here,
* so at first we silently check whether graph presents in the
* device-tree node.
*/
if (!of_graph_is_present(np))
return -ENODEV;
remote = of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
if (panel) {
*panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel))
ret = 0;
else
*panel = NULL;
}
/* No panel found yet, check for a bridge next. */
if (bridge) {
if (ret) {
*bridge = of_drm_find_bridge(remote);
if (*bridge)
ret = 0;
} else {
*bridge = NULL;
}
}
of_node_put(remote);
return ret;
}
it6122驱动会在probe中会通过drm_bridge_add将it6122->bridge添加到bridge_list中,所以这里dsi2->bridge通过of_drm_find_bridge被赋值为it6122->bridge
static int it6121_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;
struct it6121 *it6121;
int ret;
printk("====>%s,line %d\n",__func__,__LINE__);
it6121 = devm_kzalloc(dev, sizeof(*it6121), GFP_KERNEL);
if (!it6121)
return -ENOMEM;
// it6121->split_mode = of_property_read_bool(np, "split-mode");
it6121->i2c = client;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &it6121->panel, NULL);
if (ret < 0)
return ret;
if (!(it6121->panel))
return -ENODEV;
it6121->panel_bridge = devm_drm_panel_bridge_add(dev, it6121->panel);
if (IS_ERR(it6121->panel_bridge))
return PTR_ERR(it6121->panel_bridge);
it6121->regmap = devm_regmap_init_i2c(client,
&it6121_regmap_config);
if (IS_ERR(it6121->regmap)) {
ret = PTR_ERR(it6121->regmap);
goto unregister_i2c;
}
it6121->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(it6121->reset_gpio)) {
ret = PTR_ERR(it6121->reset_gpio);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get reset gpio: %d\n", ret);
goto unregister_i2c;
}
it6121->vcc1v2 = devm_gpiod_get_optional(dev, "vcc1v2",
GPIOD_ASIS);
if (IS_ERR(it6121->vcc1v2)) {
ret = PTR_ERR(it6121->vcc1v2);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get vcc1v2 gpio: %d\n", ret);
goto unregister_i2c;
}
it6121->vcc1v8 = devm_gpiod_get_optional(dev, "vcc1v8",
GPIOD_ASIS);
if (IS_ERR(it6121->vcc1v8)) {
ret = PTR_ERR(it6121->vcc1v8);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get vcc1v8 gpio: %d\n", ret);
goto unregister_i2c;
}
ret = it6121_parse_dt(dev->of_node, it6121);
if (ret) {
goto unregister_i2c;
}
//it6121->hresync = 0;
it6121->u8SysSts = 0;
ret = it6122_power_on(it6121);
if (ret) {
dev_err(dev, "Failed to power on \n");
goto unregister_i2c;
}
// disable all interrupt
regmap_write(it6121->regmap, 0x09, 0x0);
regmap_write(it6121->regmap, 0x0A, 0x0);
regmap_write(it6121->regmap, 0x0B, 0x0);
ret = it6121_check_chipid(it6121); // check ite612x dev id
if (ret)
goto unregister_i2c;
// it6121_config_reset(it6121);
// msleep(100);
#if EN_INT_MODE
thread = kthread_run(ite612x_event_handler, it6121, "synaptics_dsx");
if (IS_ERR(thread)) {
ret = PTR_ERR(thread);
DRM_INFO(" %s: failed to create kernel thread: %d\n", __func__, ret);
}
it6121->irq = client->irq;
ret = devm_request_irq(&client->dev, it6121->irq, (irq_handler_t) ite612x_intr_handler,
IRQF_TRIGGER_LOW /*IRQF_TRIGGER_FALLING*/, "ite612x_intp", it6121);
if (ret < 0) {
DRM_INFO("%s: Failed to register attention interrupt\n",
__func__);
goto unregister_i2c;
}
#endif
it6121->bridge.driver_private = it6121;
it6121->bridge.funcs = &it6121_bridge_funcs;
it6121->bridge.of_node = np;
#ifndef RASP_PI
drm_bridge_add(&it6121->bridge);
#else
it6121_attach_dsi(it6121);
it6121_init(it6121);
#endif
i2c_set_clientdata(client, it6121);
return ret;
unregister_i2c:
DRM_INFO("it6121_probe failed\n");
devm_kfree(dev, it6121);
if (ret == -EPROBE_DEFER)
return ret;
return ret;
}
回到dw_mipi_dsi2_bind函数,接下来将dsi host通过drm_simple_encoder_init,drm_encoder_helper_add注册为encoder
if (dsi2->bridge) {
struct list_head *connector_list =
&drm_dev->mode_config.connector_list;
dsi2->bridge->driver_private = &dsi2->host;
dsi2->bridge->encoder = encoder;
flags = dsi2->bridge->ops & DRM_BRIDGE_OP_MODES ?
DRM_BRIDGE_ATTACH_NO_CONNECTOR : 0;
ret = drm_bridge_attach(encoder, dsi2->bridge, NULL, flags);
if (ret) {
DRM_DEV_ERROR(dev,
"Failed to attach bridge: %d\n", ret);
goto encoder_cleanup;
}
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
list_for_each_entry(connector, connector_list, head)
if (drm_connector_has_possible_encoder(connector,
encoder))
break;
}
调用drm_bridge_attach,具体调用bridge中的attach函数,也就是it6121_bridge_attach
static int it6121_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
{
struct it6121 *it6121 = bridge_to_it6121(bridge);
struct drm_device *drm = bridge->dev;
int ret;
#if 0 //panel has found in the beginning of probe
// panel register to ite
simple_panel_host_register(it6121);
#endif
#if 1
if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
dev_err(&it6121->i2c->dev,
"it6121 driver only copes with atomic updates\n");
return -ENOTSUPP;
}
it6121->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
ret = drm_connector_init(drm, &it6121->connector,
&it6121_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
if (ret) {
dev_err(&it6121->i2c->dev,
"Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(&it6121->connector,
&it6121_connector_helper_funcs);
drm_connector_attach_encoder(&it6121->connector, bridge->encoder);
#if 0 //delete,drm_panel_attach can't find in kernel-5.10
ret = drm_panel_attach(it6121->panel, &it6121->connector);
if (ret) {
dev_err(&it6121->i2c->dev, "Failed to attach panel: %d\n", ret);
drm_connector_cleanup(&it6121->connector);
return ret;
}
#endif
#else
// create connector
#endif
// attach to dsi host (dsi bridge)
ret = it6121_attach_dsi(it6121);
// todo enable detect cable interrupt
return ret;
}
注册connector,it6121_attach_dsi中of_find_mipi_dsi_host_by_node找到对应的dsi host的节点,mipi_dsi_device_register_full注册dsi device,mipi_dsi_attach调用host的attach函数