Use devm_kzalloc instead of kzalloc and drop kfree(). Let the memory handled by driver detach.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org --- drivers/gpu/drm/bridge/analogix/anx7625.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 0b858c78abe8b6..dbe708eb3bcf11 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2515,7 +2515,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, return -ENODEV; }
- platform = kzalloc(sizeof(*platform), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); if (!platform) { DRM_DEV_ERROR(dev, "fail to allocate driver data\n"); return -ENOMEM; @@ -2527,7 +2527,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (ret) { if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); - goto free_platform; + return ret; }
platform->client = client; @@ -2552,7 +2552,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (!platform->hdcp_workqueue) { dev_err(dev, "fail to create work queue\n"); ret = -ENOMEM; - goto free_platform; + return ret; }
platform->pdata.intp_irq = client->irq; @@ -2637,9 +2637,6 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (platform->hdcp_workqueue) destroy_workqueue(platform->hdcp_workqueue);
-free_platform: - kfree(platform); - return ret; }
Support reading edid through aux channel if panel is connected to aux bus. Extend anx7625_aux_dpcd_trans() to implement aux transfer function:
1. panel is populated in devm_of_dp_aux_populate_ep_devices(), so move anx7625_parse_dt() after. 2. Use pm runtime autosuspend since aux transfer function is called multiple times when reading edid. 3. No-op if aux transfer length is 0.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org --- This patch is based on drm-misc-next and depends on https://patchwork.freedesktop.org/patch/469081/ --- drivers/gpu/drm/bridge/analogix/anx7625.c | 123 ++++++++++++++++++---- drivers/gpu/drm/bridge/analogix/anx7625.h | 4 + 2 files changed, 109 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index dbe708eb3bcf11..d60cfc058fbe7d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -24,6 +24,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_aux_bus.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_hdcp.h> @@ -231,19 +232,22 @@ static int wait_aux_op_finish(struct anx7625_data *ctx) return 0; }
-static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, - u32 address, u8 len, u8 *buf) +static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address, + u8 len, u8 *buf) { struct device *dev = &ctx->client->dev; int ret; u8 addrh, addrm, addrl; u8 cmd;
- if (len > MAX_DPCD_BUFFER_SIZE) { + if (len > DP_AUX_MAX_PAYLOAD_BYTES) { dev_err(dev, "exceed aux buffer len.\n"); return -EINVAL; }
+ if (!len) + return len; + addrl = address & 0xFF; addrm = (address >> 8) & 0xFF; addrh = (address >> 16) & 0xFF; @@ -262,7 +266,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_ADDR_19_16, addrh);
- if (op == DPCD_WRITE) + if (op == DPCD_WRITE || op == AP_AUX_WRITE) ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_BUFF_START, len, buf); /* Enable aux access */ @@ -275,14 +279,14 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, }
ret = wait_aux_op_finish(ctx); - if (ret) { + if (ret < 0) { dev_err(dev, "aux IO error: wait aux op finish.\n"); return ret; }
/* Write done */ - if (op == DPCD_WRITE) - return 0; + if (op == DPCD_WRITE || op == AP_AUX_WRITE) + return len;
/* Read done, read out dpcd data */ ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, @@ -292,7 +296,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, return -EIO; }
- return 0; + return len; }
static int anx7625_video_mute_control(struct anx7625_data *ctx, @@ -867,7 +871,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx) }
/* Read downstream capability */ - anx7625_aux_dpcd_trans(ctx, DPCD_READ, 0x68028, 1, &bcap); + anx7625_aux_trans(ctx, DPCD_READ, 0x68028, 1, &bcap); if (!(bcap & 0x01)) { pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); return 0; @@ -956,7 +960,7 @@ static void anx7625_dp_stop(struct anx7625_data *ctx) dev_dbg(dev, "notify downstream enter into standby\n"); /* Downstream monitor enter into standby mode */ data = 2; - ret |= anx7625_aux_dpcd_trans(ctx, DPCD_WRITE, 0x000600, 1, &data); + ret |= anx7625_aux_trans(ctx, DPCD_WRITE, 0x000600, 1, &data); if (ret < 0) DRM_DEV_ERROR(dev, "IO error : mute video fail\n");
@@ -1655,11 +1659,60 @@ static int anx7625_parse_dt(struct device *dev, return 0; }
+static bool anx7625_of_panel_on_aux_bus(struct device *dev) +{ + struct device_node *bus, *panel; + + bus = of_get_child_by_name(dev->of_node, "aux-bus"); + if (!bus) + return false; + + panel = of_get_child_by_name(bus, "panel"); + of_node_put(bus); + if (!panel) + return false; + of_node_put(panel); + + return true; +} + static inline struct anx7625_data *bridge_to_anx7625(struct drm_bridge *bridge) { return container_of(bridge, struct anx7625_data, bridge); }
+static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct anx7625_data *ctx = container_of(aux, struct anx7625_data, aux); + struct device *dev = &ctx->client->dev; + u8 request = msg->request & ~DP_AUX_I2C_MOT, op; + int ret = 0; + + pm_runtime_get_sync(dev); + msg->reply = 0; + switch (request) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + op = AP_AUX_WRITE; + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + op = AP_AUX_READ; + break; + default: + ret = -EINVAL; + } + if (!ret) { + ret = anx7625_aux_trans(ctx, op, msg->address, msg->size, + msg->buffer); + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static struct edid *anx7625_get_edid(struct anx7625_data *ctx) { struct device *dev = &ctx->client->dev; @@ -2066,6 +2119,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return -ENODEV; }
+ ctx->aux.drm_dev = bridge->dev; + err = drm_dp_aux_register(&ctx->aux); + if (err) { + dev_err(dev, "failed to register aux channel: %d\n", err); + return err; + } + if (ctx->pdata.panel_bridge) { err = drm_bridge_attach(bridge->encoder, ctx->pdata.panel_bridge, @@ -2079,6 +2139,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return 0; }
+static void anx7625_bridge_detach(struct drm_bridge *bridge) +{ + struct anx7625_data *ctx = bridge_to_anx7625(bridge); + + drm_dp_aux_unregister(&ctx->aux); +} + static enum drm_mode_status anx7625_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -2344,6 +2411,7 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs anx7625_bridge_funcs = { .attach = anx7625_bridge_attach, + .detach = anx7625_bridge_detach, .mode_valid = anx7625_bridge_mode_valid, .mode_set = anx7625_bridge_mode_set, .atomic_check = anx7625_bridge_atomic_check, @@ -2501,6 +2569,12 @@ static const struct dev_pm_ops anx7625_pm_ops = { anx7625_runtime_pm_resume, NULL) };
+static void anx7625_runtime_disable(void *data) +{ + pm_runtime_dont_use_autosuspend(data); + pm_runtime_disable(data); +} + static int anx7625_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2523,13 +2597,6 @@ static int anx7625_i2c_probe(struct i2c_client *client,
pdata = &platform->pdata;
- ret = anx7625_parse_dt(dev, pdata); - if (ret) { - if (ret != -EPROBE_DEFER) - DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); - return ret; - } - platform->client = client; i2c_set_clientdata(client, platform);
@@ -2577,6 +2644,19 @@ static int anx7625_i2c_probe(struct i2c_client *client, } }
+ platform->aux.name = "anx7625-aux"; + platform->aux.dev = dev; + platform->aux.transfer = anx7625_aux_transfer; + drm_dp_aux_init(&platform->aux); + devm_of_dp_aux_populate_ep_devices(&platform->aux); + + ret = anx7625_parse_dt(dev, pdata); + if (ret) { + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret); + return ret; + } + if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { ret = -ENOMEM; DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n"); @@ -2584,6 +2664,12 @@ static int anx7625_i2c_probe(struct i2c_client *client, }
pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_suspend_ignore_children(dev, true); + ret = devm_add_action_or_reset(dev, anx7625_runtime_disable, dev); + if (ret) + return ret;
if (!platform->pdata.low_power_mode) { anx7625_disable_pd_protocol(platform); @@ -2596,7 +2682,8 @@ static int anx7625_i2c_probe(struct i2c_client *client,
platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node; - platform->bridge.ops = DRM_BRIDGE_OP_EDID; + if (!anx7625_of_panel_on_aux_bus(&client->dev)) + platform->bridge.ops |= DRM_BRIDGE_OP_EDID; if (!platform->pdata.panel_bridge) platform->bridge.ops |= DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_DETECT; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 56165f5b254c14..cab19735c22e21 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -231,6 +231,9 @@ #define AP_AUX_ADDR_15_8 0x12 #define AP_AUX_ADDR_19_16 0x13
+#define AP_AUX_READ 0x1 +#define AP_AUX_WRITE 0x4 + /* Bit[0:3] AUX status, bit 4 op_en, bit 5 address only */ #define AP_AUX_CTRL_STATUS 0x14 #define AP_AUX_CTRL_OP_EN 0x10 @@ -474,6 +477,7 @@ struct anx7625_data { u8 bridge_attached; struct drm_connector *connector; struct mipi_dsi_device *dsi; + struct drm_dp_aux aux; };
#endif /* __ANX7625_H__ */
Hi Hsin-Yi, thanks for the patch! Please fix the follow comment, then add my id. Reviewed-by: Xin Ji xji@analogixsemi.com
On Tue, Jan 11, 2022 at 07:27:00PM +0800, Hsin-Yi Wang wrote:
Support reading edid through aux channel if panel is connected to aux bus. Extend anx7625_aux_dpcd_trans() to implement aux transfer function:
- panel is populated in devm_of_dp_aux_populate_ep_devices(), so move anx7625_parse_dt() after.
- Use pm runtime autosuspend since aux transfer function is called multiple times when reading edid.
- No-op if aux transfer length is 0.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org
This patch is based on drm-misc-next and depends on https://patchwork.freedesktop.org/patch/469081/
drivers/gpu/drm/bridge/analogix/anx7625.c | 123 ++++++++++++++++++---- drivers/gpu/drm/bridge/analogix/anx7625.h | 4 + 2 files changed, 109 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index dbe708eb3bcf11..d60cfc058fbe7d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -24,6 +24,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_aux_bus.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_hdcp.h> @@ -231,19 +232,22 @@ static int wait_aux_op_finish(struct anx7625_data *ctx) return 0; }
-static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op,
u32 address, u8 len, u8 *buf)
+static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address,
u8 len, u8 *buf)
{ struct device *dev = &ctx->client->dev; int ret; u8 addrh, addrm, addrl; u8 cmd;
- if (len > MAX_DPCD_BUFFER_SIZE) {
- if (len > DP_AUX_MAX_PAYLOAD_BYTES) { dev_err(dev, "exceed aux buffer len.\n"); return -EINVAL;
The native write command is 0x08, I2C Over aux write command is 0x04. The native read command is 0x09, I2C Over aux read command is 0x05.
}
- if (!len)
return len;
- addrl = address & 0xFF; addrm = (address >> 8) & 0xFF; addrh = (address >> 16) & 0xFF;
@@ -262,7 +266,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_ADDR_19_16, addrh);
- if (op == DPCD_WRITE)
- if (op == DPCD_WRITE || op == AP_AUX_WRITE) ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_BUFF_START, len, buf); /* Enable aux access */
@@ -275,14 +279,14 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, }
ret = wait_aux_op_finish(ctx);
- if (ret) {
if (ret < 0) { dev_err(dev, "aux IO error: wait aux op finish.\n"); return ret; }
/* Write done */
- if (op == DPCD_WRITE)
return 0;
if (op == DPCD_WRITE || op == AP_AUX_WRITE)
return len;
/* Read done, read out dpcd data */ ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
@@ -292,7 +296,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, return -EIO; }
- return 0;
- return len;
}
static int anx7625_video_mute_control(struct anx7625_data *ctx, @@ -867,7 +871,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx) }
/* Read downstream capability */
- anx7625_aux_dpcd_trans(ctx, DPCD_READ, 0x68028, 1, &bcap);
- anx7625_aux_trans(ctx, DPCD_READ, 0x68028, 1, &bcap); if (!(bcap & 0x01)) { pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); return 0;
@@ -956,7 +960,7 @@ static void anx7625_dp_stop(struct anx7625_data *ctx) dev_dbg(dev, "notify downstream enter into standby\n"); /* Downstream monitor enter into standby mode */ data = 2;
- ret |= anx7625_aux_dpcd_trans(ctx, DPCD_WRITE, 0x000600, 1, &data);
- ret |= anx7625_aux_trans(ctx, DPCD_WRITE, 0x000600, 1, &data); if (ret < 0) DRM_DEV_ERROR(dev, "IO error : mute video fail\n");
@@ -1655,11 +1659,60 @@ static int anx7625_parse_dt(struct device *dev, return 0; }
+static bool anx7625_of_panel_on_aux_bus(struct device *dev) +{
- struct device_node *bus, *panel;
- bus = of_get_child_by_name(dev->of_node, "aux-bus");
- if (!bus)
return false;
- panel = of_get_child_by_name(bus, "panel");
- of_node_put(bus);
- if (!panel)
return false;
- of_node_put(panel);
- return true;
+}
static inline struct anx7625_data *bridge_to_anx7625(struct drm_bridge *bridge) { return container_of(bridge, struct anx7625_data, bridge); }
+static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg)
+{
- struct anx7625_data *ctx = container_of(aux, struct anx7625_data, aux);
- struct device *dev = &ctx->client->dev;
- u8 request = msg->request & ~DP_AUX_I2C_MOT, op;
- int ret = 0;
- pm_runtime_get_sync(dev);
- msg->reply = 0;
- switch (request) {
- case DP_AUX_NATIVE_WRITE:
- case DP_AUX_I2C_WRITE:
op = AP_AUX_WRITE;
The DPCD native write is different with I2C over aux write.
break;
- case DP_AUX_NATIVE_READ:
- case DP_AUX_I2C_READ:
op = AP_AUX_READ;
The DPCD native read is different with I2C over aux read.
break;
- default:
ret = -EINVAL;
- }
- if (!ret) {
ret = anx7625_aux_trans(ctx, op, msg->address, msg->size,
msg->buffer);
- }
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
- return ret;
+}
static struct edid *anx7625_get_edid(struct anx7625_data *ctx) { struct device *dev = &ctx->client->dev; @@ -2066,6 +2119,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return -ENODEV; }
- ctx->aux.drm_dev = bridge->dev;
- err = drm_dp_aux_register(&ctx->aux);
- if (err) {
dev_err(dev, "failed to register aux channel: %d\n", err);
return err;
- }
- if (ctx->pdata.panel_bridge) { err = drm_bridge_attach(bridge->encoder, ctx->pdata.panel_bridge,
@@ -2079,6 +2139,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return 0; }
+static void anx7625_bridge_detach(struct drm_bridge *bridge) +{
- struct anx7625_data *ctx = bridge_to_anx7625(bridge);
- drm_dp_aux_unregister(&ctx->aux);
+}
static enum drm_mode_status anx7625_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -2344,6 +2411,7 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs anx7625_bridge_funcs = { .attach = anx7625_bridge_attach,
- .detach = anx7625_bridge_detach, .mode_valid = anx7625_bridge_mode_valid, .mode_set = anx7625_bridge_mode_set, .atomic_check = anx7625_bridge_atomic_check,
@@ -2501,6 +2569,12 @@ static const struct dev_pm_ops anx7625_pm_ops = { anx7625_runtime_pm_resume, NULL) };
+static void anx7625_runtime_disable(void *data) +{
- pm_runtime_dont_use_autosuspend(data);
- pm_runtime_disable(data);
+}
static int anx7625_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2523,13 +2597,6 @@ static int anx7625_i2c_probe(struct i2c_client *client,
pdata = &platform->pdata;
- ret = anx7625_parse_dt(dev, pdata);
- if (ret) {
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret);
return ret;
- }
- platform->client = client; i2c_set_clientdata(client, platform);
@@ -2577,6 +2644,19 @@ static int anx7625_i2c_probe(struct i2c_client *client, } }
- platform->aux.name = "anx7625-aux";
- platform->aux.dev = dev;
- platform->aux.transfer = anx7625_aux_transfer;
- drm_dp_aux_init(&platform->aux);
- devm_of_dp_aux_populate_ep_devices(&platform->aux);
- ret = anx7625_parse_dt(dev, pdata);
- if (ret) {
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret);
return ret;
- }
- if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { ret = -ENOMEM; DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n");
@@ -2584,6 +2664,12 @@ static int anx7625_i2c_probe(struct i2c_client *client, }
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
pm_suspend_ignore_children(dev, true);
ret = devm_add_action_or_reset(dev, anx7625_runtime_disable, dev);
if (ret)
return ret;
if (!platform->pdata.low_power_mode) { anx7625_disable_pd_protocol(platform);
@@ -2596,7 +2682,8 @@ static int anx7625_i2c_probe(struct i2c_client *client,
platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node;
- platform->bridge.ops = DRM_BRIDGE_OP_EDID;
- if (!anx7625_of_panel_on_aux_bus(&client->dev))
if (!platform->pdata.panel_bridge) platform->bridge.ops |= DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_DETECT;platform->bridge.ops |= DRM_BRIDGE_OP_EDID;
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 56165f5b254c14..cab19735c22e21 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -231,6 +231,9 @@ #define AP_AUX_ADDR_15_8 0x12 #define AP_AUX_ADDR_19_16 0x13
+#define AP_AUX_READ 0x1 +#define AP_AUX_WRITE 0x4
/* Bit[0:3] AUX status, bit 4 op_en, bit 5 address only */ #define AP_AUX_CTRL_STATUS 0x14 #define AP_AUX_CTRL_OP_EN 0x10 @@ -474,6 +477,7 @@ struct anx7625_data { u8 bridge_attached; struct drm_connector *connector; struct mipi_dsi_device *dsi;
- struct drm_dp_aux aux;
};
#endif /* __ANX7625_H__ */
2.34.1.575.g55b058a8bb-goog
On Thu, Jan 13, 2022 at 11:40 AM Xin Ji xji@analogixsemi.com wrote:
Hi Hsin-Yi, thanks for the patch! Please fix the follow comment, then add my id. Reviewed-by: Xin Ji xji@analogixsemi.com
On Tue, Jan 11, 2022 at 07:27:00PM +0800, Hsin-Yi Wang wrote:
Support reading edid through aux channel if panel is connected to aux bus. Extend anx7625_aux_dpcd_trans() to implement aux transfer function:
- panel is populated in devm_of_dp_aux_populate_ep_devices(), so move anx7625_parse_dt() after.
- Use pm runtime autosuspend since aux transfer function is called multiple times when reading edid.
- No-op if aux transfer length is 0.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org
This patch is based on drm-misc-next and depends on https://patchwork.freedesktop.org/patch/469081/
drivers/gpu/drm/bridge/analogix/anx7625.c | 123 ++++++++++++++++++---- drivers/gpu/drm/bridge/analogix/anx7625.h | 4 + 2 files changed, 109 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index dbe708eb3bcf11..d60cfc058fbe7d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -24,6 +24,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_aux_bus.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_hdcp.h> @@ -231,19 +232,22 @@ static int wait_aux_op_finish(struct anx7625_data *ctx) return 0; }
-static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op,
u32 address, u8 len, u8 *buf)
+static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address,
u8 len, u8 *buf)
{ struct device *dev = &ctx->client->dev; int ret; u8 addrh, addrm, addrl; u8 cmd;
if (len > MAX_DPCD_BUFFER_SIZE) {
if (len > DP_AUX_MAX_PAYLOAD_BYTES) { dev_err(dev, "exceed aux buffer len.\n"); return -EINVAL;
The native write command is 0x08, I2C Over aux write command is 0x04. The native read command is 0x09, I2C Over aux read command is 0x05.
Thanks for the review, the command for native read/write is updated.
v2 is sent.
}
if (!len)
return len;
addrl = address & 0xFF; addrm = (address >> 8) & 0xFF; addrh = (address >> 16) & 0xFF;
@@ -262,7 +266,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_ADDR_19_16, addrh);
if (op == DPCD_WRITE)
if (op == DPCD_WRITE || op == AP_AUX_WRITE) ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client, AP_AUX_BUFF_START, len, buf); /* Enable aux access */
@@ -275,14 +279,14 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, }
ret = wait_aux_op_finish(ctx);
if (ret) {
if (ret < 0) { dev_err(dev, "aux IO error: wait aux op finish.\n"); return ret; } /* Write done */
if (op == DPCD_WRITE)
return 0;
if (op == DPCD_WRITE || op == AP_AUX_WRITE)
return len; /* Read done, read out dpcd data */ ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
@@ -292,7 +296,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, return -EIO; }
return 0;
return len;
}
static int anx7625_video_mute_control(struct anx7625_data *ctx, @@ -867,7 +871,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx) }
/* Read downstream capability */
anx7625_aux_dpcd_trans(ctx, DPCD_READ, 0x68028, 1, &bcap);
anx7625_aux_trans(ctx, DPCD_READ, 0x68028, 1, &bcap); if (!(bcap & 0x01)) { pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); return 0;
@@ -956,7 +960,7 @@ static void anx7625_dp_stop(struct anx7625_data *ctx) dev_dbg(dev, "notify downstream enter into standby\n"); /* Downstream monitor enter into standby mode */ data = 2;
ret |= anx7625_aux_dpcd_trans(ctx, DPCD_WRITE, 0x000600, 1, &data);
ret |= anx7625_aux_trans(ctx, DPCD_WRITE, 0x000600, 1, &data); if (ret < 0) DRM_DEV_ERROR(dev, "IO error : mute video fail\n");
@@ -1655,11 +1659,60 @@ static int anx7625_parse_dt(struct device *dev, return 0; }
+static bool anx7625_of_panel_on_aux_bus(struct device *dev) +{
struct device_node *bus, *panel;
bus = of_get_child_by_name(dev->of_node, "aux-bus");
if (!bus)
return false;
panel = of_get_child_by_name(bus, "panel");
of_node_put(bus);
if (!panel)
return false;
of_node_put(panel);
return true;
+}
static inline struct anx7625_data *bridge_to_anx7625(struct drm_bridge *bridge) { return container_of(bridge, struct anx7625_data, bridge); }
+static ssize_t anx7625_aux_transfer(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg)
+{
struct anx7625_data *ctx = container_of(aux, struct anx7625_data, aux);
struct device *dev = &ctx->client->dev;
u8 request = msg->request & ~DP_AUX_I2C_MOT, op;
int ret = 0;
pm_runtime_get_sync(dev);
msg->reply = 0;
switch (request) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
op = AP_AUX_WRITE;
The DPCD native write is different with I2C over aux write.
break;
case DP_AUX_NATIVE_READ:
case DP_AUX_I2C_READ:
op = AP_AUX_READ;
The DPCD native read is different with I2C over aux read.
break;
default:
ret = -EINVAL;
}
if (!ret) {
ret = anx7625_aux_trans(ctx, op, msg->address, msg->size,
msg->buffer);
}
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
+}
static struct edid *anx7625_get_edid(struct anx7625_data *ctx) { struct device *dev = &ctx->client->dev; @@ -2066,6 +2119,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return -ENODEV; }
ctx->aux.drm_dev = bridge->dev;
err = drm_dp_aux_register(&ctx->aux);
if (err) {
dev_err(dev, "failed to register aux channel: %d\n", err);
return err;
}
if (ctx->pdata.panel_bridge) { err = drm_bridge_attach(bridge->encoder, ctx->pdata.panel_bridge,
@@ -2079,6 +2139,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, return 0; }
+static void anx7625_bridge_detach(struct drm_bridge *bridge) +{
struct anx7625_data *ctx = bridge_to_anx7625(bridge);
drm_dp_aux_unregister(&ctx->aux);
+}
static enum drm_mode_status anx7625_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, @@ -2344,6 +2411,7 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs anx7625_bridge_funcs = { .attach = anx7625_bridge_attach,
.detach = anx7625_bridge_detach, .mode_valid = anx7625_bridge_mode_valid, .mode_set = anx7625_bridge_mode_set, .atomic_check = anx7625_bridge_atomic_check,
@@ -2501,6 +2569,12 @@ static const struct dev_pm_ops anx7625_pm_ops = { anx7625_runtime_pm_resume, NULL) };
+static void anx7625_runtime_disable(void *data) +{
pm_runtime_dont_use_autosuspend(data);
pm_runtime_disable(data);
+}
static int anx7625_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2523,13 +2597,6 @@ static int anx7625_i2c_probe(struct i2c_client *client,
pdata = &platform->pdata;
ret = anx7625_parse_dt(dev, pdata);
if (ret) {
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret);
return ret;
}
platform->client = client; i2c_set_clientdata(client, platform);
@@ -2577,6 +2644,19 @@ static int anx7625_i2c_probe(struct i2c_client *client, } }
platform->aux.name = "anx7625-aux";
platform->aux.dev = dev;
platform->aux.transfer = anx7625_aux_transfer;
drm_dp_aux_init(&platform->aux);
devm_of_dp_aux_populate_ep_devices(&platform->aux);
ret = anx7625_parse_dt(dev, pdata);
if (ret) {
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret);
return ret;
}
if (anx7625_register_i2c_dummy_clients(platform, client) != 0) { ret = -ENOMEM; DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n");
@@ -2584,6 +2664,12 @@ static int anx7625_i2c_probe(struct i2c_client *client, }
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
pm_suspend_ignore_children(dev, true);
ret = devm_add_action_or_reset(dev, anx7625_runtime_disable, dev);
if (ret)
return ret; if (!platform->pdata.low_power_mode) { anx7625_disable_pd_protocol(platform);
@@ -2596,7 +2682,8 @@ static int anx7625_i2c_probe(struct i2c_client *client,
platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node;
platform->bridge.ops = DRM_BRIDGE_OP_EDID;
if (!anx7625_of_panel_on_aux_bus(&client->dev))
platform->bridge.ops |= DRM_BRIDGE_OP_EDID; if (!platform->pdata.panel_bridge) platform->bridge.ops |= DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_DETECT;
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 56165f5b254c14..cab19735c22e21 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -231,6 +231,9 @@ #define AP_AUX_ADDR_15_8 0x12 #define AP_AUX_ADDR_19_16 0x13
+#define AP_AUX_READ 0x1 +#define AP_AUX_WRITE 0x4
/* Bit[0:3] AUX status, bit 4 op_en, bit 5 address only */ #define AP_AUX_CTRL_STATUS 0x14 #define AP_AUX_CTRL_OP_EN 0x10 @@ -474,6 +477,7 @@ struct anx7625_data { u8 bridge_attached; struct drm_connector *connector; struct mipi_dsi_device *dsi;
struct drm_dp_aux aux;
};
#endif /* __ANX7625_H__ */
2.34.1.575.g55b058a8bb-goog
List panel under aux-bus node if it's connected to anx7625's aux bus.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org --- .../display/bridge/analogix,anx7625.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml index 1d3e88daca041a..0d38d6fe39830f 100644 --- a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml +++ b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml @@ -83,6 +83,9 @@ properties: type: boolean description: let the driver enable audio HDMI codec function or not.
+ aux-bus: + $ref: /schemas/display/dp-aux-bus.yaml# + ports: $ref: /schemas/graph.yaml#/properties/ports
@@ -167,5 +170,19 @@ examples: }; }; }; + + aux-bus { + panel { + compatible = "innolux,n125hce-gn1"; + power-supply = <&pp3300_disp_x>; + backlight = <&backlight_lcd0>; + + port { + panel_in: endpoint { + remote-endpoint = <&anx7625_out>; + }; + }; + }; + }; }; };
Hi Hsin-Yi, thanks for the patch! It's OK for me! Reviewed-by: Xin Ji xji@analogixsemi.com
Thanks, Xin
On Tue, Jan 11, 2022 at 07:27:01PM +0800, Hsin-Yi Wang wrote:
List panel under aux-bus node if it's connected to anx7625's aux bus.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org
.../display/bridge/analogix,anx7625.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml index 1d3e88daca041a..0d38d6fe39830f 100644 --- a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml +++ b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml @@ -83,6 +83,9 @@ properties: type: boolean description: let the driver enable audio HDMI codec function or not.
- aux-bus:
- $ref: /schemas/display/dp-aux-bus.yaml#
- ports: $ref: /schemas/graph.yaml#/properties/ports
@@ -167,5 +170,19 @@ examples: }; }; };
aux-bus {
panel {
compatible = "innolux,n125hce-gn1";
power-supply = <&pp3300_disp_x>;
backlight = <&backlight_lcd0>;
port {
panel_in: endpoint {
remote-endpoint = <&anx7625_out>;
};
};
};
};}; };
-- 2.34.1.575.g55b058a8bb-goog
Hi Hsin-Yi, thanks for the patch! Reviewed-by: Xin Ji xji@analogixsemi.com
Thanks, Xin
On Tue, Jan 11, 2022 at 07:26:59PM +0800, Hsin-Yi Wang wrote:
Use devm_kzalloc instead of kzalloc and drop kfree(). Let the memory handled by driver detach.
Signed-off-by: Hsin-Yi Wang hsinyi@chromium.org
drivers/gpu/drm/bridge/analogix/anx7625.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 0b858c78abe8b6..dbe708eb3bcf11 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2515,7 +2515,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, return -ENODEV; }
- platform = kzalloc(sizeof(*platform), GFP_KERNEL);
- platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); if (!platform) { DRM_DEV_ERROR(dev, "fail to allocate driver data\n"); return -ENOMEM;
@@ -2527,7 +2527,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (ret) { if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "fail to parse DT : %d\n", ret);
goto free_platform;
return ret;
}
platform->client = client;
@@ -2552,7 +2552,7 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (!platform->hdcp_workqueue) { dev_err(dev, "fail to create work queue\n"); ret = -ENOMEM;
goto free_platform;
return ret;
}
platform->pdata.intp_irq = client->irq;
@@ -2637,9 +2637,6 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (platform->hdcp_workqueue) destroy_workqueue(platform->hdcp_workqueue);
-free_platform:
- kfree(platform);
- return ret;
}
-- 2.34.1.575.g55b058a8bb-goog
dri-devel@lists.freedesktop.org