Commit adca62ec authored by Hsin-Yi Wang's avatar Hsin-Yi Wang Committed by Robert Foss

drm/bridge: anx7625: Support reading edid through aux channel

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: default avatarHsin-Yi Wang <hsinyi@chromium.org>
Reviewed-by: default avatarXin Ji <xji@analogixsemi.com>
Reported-by: default avatarkernel test robot <lkp@intel.com>
Signed-off-by: default avatarRobert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220213103437.3363848-3-hsinyi@chromium.org
parent 57bfb34a
...@@ -32,6 +32,8 @@ config DRM_ANALOGIX_ANX7625 ...@@ -32,6 +32,8 @@ config DRM_ANALOGIX_ANX7625
tristate "Analogix Anx7625 MIPI to DP interface support" tristate "Analogix Anx7625 MIPI to DP interface support"
depends on DRM depends on DRM
depends on OF depends on OF
select DRM_DP_AUX_BUS
select DRM_DP_HELPER
select DRM_MIPI_DSI select DRM_MIPI_DSI
help help
ANX7625 is an ultra-low power 4K mobile HD transmitter ANX7625 is an ultra-low power 4K mobile HD transmitter
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h> #include <drm/drm_bridge.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/dp/drm_dp_aux_bus.h>
#include <drm/dp/drm_dp_helper.h> #include <drm/dp/drm_dp_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_hdcp.h> #include <drm/drm_hdcp.h>
...@@ -231,19 +232,23 @@ static int wait_aux_op_finish(struct anx7625_data *ctx) ...@@ -231,19 +232,23 @@ static int wait_aux_op_finish(struct anx7625_data *ctx)
return 0; return 0;
} }
static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, static int anx7625_aux_trans(struct anx7625_data *ctx, u8 op, u32 address,
u32 address, u8 len, u8 *buf) u8 len, u8 *buf)
{ {
struct device *dev = &ctx->client->dev; struct device *dev = &ctx->client->dev;
int ret; int ret;
u8 addrh, addrm, addrl; u8 addrh, addrm, addrl;
u8 cmd; u8 cmd;
bool is_write = !(op & DP_AUX_I2C_READ);
if (len > MAX_DPCD_BUFFER_SIZE) { if (len > DP_AUX_MAX_PAYLOAD_BYTES) {
dev_err(dev, "exceed aux buffer len.\n"); dev_err(dev, "exceed aux buffer len.\n");
return -EINVAL; return -EINVAL;
} }
if (!len)
return len;
addrl = address & 0xFF; addrl = address & 0xFF;
addrm = (address >> 8) & 0xFF; addrm = (address >> 8) & 0xFF;
addrh = (address >> 16) & 0xFF; addrh = (address >> 16) & 0xFF;
...@@ -262,7 +267,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ...@@ -262,7 +267,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op,
ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
AP_AUX_ADDR_19_16, addrh); AP_AUX_ADDR_19_16, addrh);
if (op == DP_AUX_NATIVE_WRITE) if (is_write)
ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client, ret |= anx7625_reg_block_write(ctx, ctx->i2c.rx_p0_client,
AP_AUX_BUFF_START, len, buf); AP_AUX_BUFF_START, len, buf);
/* Enable aux access */ /* Enable aux access */
...@@ -275,14 +280,14 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ...@@ -275,14 +280,14 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op,
} }
ret = wait_aux_op_finish(ctx); ret = wait_aux_op_finish(ctx);
if (ret) { if (ret < 0) {
dev_err(dev, "aux IO error: wait aux op finish.\n"); dev_err(dev, "aux IO error: wait aux op finish.\n");
return ret; return ret;
} }
/* Write done */ /* Write done */
if (op == DP_AUX_NATIVE_WRITE) if (is_write)
return 0; return len;
/* Read done, read out dpcd data */ /* Read done, read out dpcd data */
ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
...@@ -292,7 +297,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op, ...@@ -292,7 +297,7 @@ static int anx7625_aux_dpcd_trans(struct anx7625_data *ctx, u8 op,
return -EIO; return -EIO;
} }
return 0; return len;
} }
static int anx7625_video_mute_control(struct anx7625_data *ctx, static int anx7625_video_mute_control(struct anx7625_data *ctx,
...@@ -867,7 +872,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx) ...@@ -867,7 +872,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx)
} }
/* Read downstream capability */ /* Read downstream capability */
anx7625_aux_dpcd_trans(ctx, DP_AUX_NATIVE_READ, 0x68028, 1, &bcap); anx7625_aux_trans(ctx, DP_AUX_NATIVE_READ, 0x68028, 1, &bcap);
if (!(bcap & 0x01)) { if (!(bcap & 0x01)) {
pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap); pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap);
return 0; return 0;
...@@ -956,7 +961,7 @@ static void anx7625_dp_stop(struct anx7625_data *ctx) ...@@ -956,7 +961,7 @@ static void anx7625_dp_stop(struct anx7625_data *ctx)
dev_dbg(dev, "notify downstream enter into standby\n"); dev_dbg(dev, "notify downstream enter into standby\n");
/* Downstream monitor enter into standby mode */ /* Downstream monitor enter into standby mode */
data = 2; data = 2;
ret |= anx7625_aux_dpcd_trans(ctx, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data); ret |= anx7625_aux_trans(ctx, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data);
if (ret < 0) if (ret < 0)
DRM_DEV_ERROR(dev, "IO error : mute video fail\n"); DRM_DEV_ERROR(dev, "IO error : mute video fail\n");
...@@ -1655,11 +1660,56 @@ static int anx7625_parse_dt(struct device *dev, ...@@ -1655,11 +1660,56 @@ static int anx7625_parse_dt(struct device *dev,
return 0; 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) static inline struct anx7625_data *bridge_to_anx7625(struct drm_bridge *bridge)
{ {
return container_of(bridge, struct anx7625_data, 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;
int ret = 0;
pm_runtime_get_sync(dev);
msg->reply = 0;
switch (request) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
case DP_AUX_NATIVE_READ:
case DP_AUX_I2C_READ:
break;
default:
ret = -EINVAL;
}
if (!ret)
ret = anx7625_aux_trans(ctx, msg->request, 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) static struct edid *anx7625_get_edid(struct anx7625_data *ctx)
{ {
struct device *dev = &ctx->client->dev; struct device *dev = &ctx->client->dev;
...@@ -2066,6 +2116,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, ...@@ -2066,6 +2116,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge,
return -ENODEV; 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) { if (ctx->pdata.panel_bridge) {
err = drm_bridge_attach(bridge->encoder, err = drm_bridge_attach(bridge->encoder,
ctx->pdata.panel_bridge, ctx->pdata.panel_bridge,
...@@ -2079,6 +2136,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge, ...@@ -2079,6 +2136,13 @@ static int anx7625_bridge_attach(struct drm_bridge *bridge,
return 0; 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 static enum drm_mode_status
anx7625_bridge_mode_valid(struct drm_bridge *bridge, anx7625_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info, const struct drm_display_info *info,
...@@ -2344,6 +2408,7 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge, ...@@ -2344,6 +2408,7 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,
static const struct drm_bridge_funcs anx7625_bridge_funcs = { static const struct drm_bridge_funcs anx7625_bridge_funcs = {
.attach = anx7625_bridge_attach, .attach = anx7625_bridge_attach,
.detach = anx7625_bridge_detach,
.mode_valid = anx7625_bridge_mode_valid, .mode_valid = anx7625_bridge_mode_valid,
.mode_set = anx7625_bridge_mode_set, .mode_set = anx7625_bridge_mode_set,
.atomic_check = anx7625_bridge_atomic_check, .atomic_check = anx7625_bridge_atomic_check,
...@@ -2501,6 +2566,12 @@ static const struct dev_pm_ops anx7625_pm_ops = { ...@@ -2501,6 +2566,12 @@ static const struct dev_pm_ops anx7625_pm_ops = {
anx7625_runtime_pm_resume, NULL) 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, static int anx7625_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -2523,13 +2594,6 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -2523,13 +2594,6 @@ static int anx7625_i2c_probe(struct i2c_client *client,
pdata = &platform->pdata; 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; platform->client = client;
i2c_set_clientdata(client, platform); i2c_set_clientdata(client, platform);
...@@ -2577,6 +2641,19 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -2577,6 +2641,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) { if (anx7625_register_i2c_dummy_clients(platform, client) != 0) {
ret = -ENOMEM; ret = -ENOMEM;
DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n"); DRM_DEV_ERROR(dev, "fail to reserve I2C bus.\n");
...@@ -2584,6 +2661,12 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -2584,6 +2661,12 @@ static int anx7625_i2c_probe(struct i2c_client *client,
} }
pm_runtime_enable(dev); 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) { if (!platform->pdata.low_power_mode) {
anx7625_disable_pd_protocol(platform); anx7625_disable_pd_protocol(platform);
...@@ -2596,7 +2679,8 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -2596,7 +2679,8 @@ static int anx7625_i2c_probe(struct i2c_client *client,
platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.funcs = &anx7625_bridge_funcs;
platform->bridge.of_node = client->dev.of_node; 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) if (!platform->pdata.panel_bridge)
platform->bridge.ops |= DRM_BRIDGE_OP_HPD | platform->bridge.ops |= DRM_BRIDGE_OP_HPD |
DRM_BRIDGE_OP_DETECT; DRM_BRIDGE_OP_DETECT;
......
...@@ -472,6 +472,7 @@ struct anx7625_data { ...@@ -472,6 +472,7 @@ struct anx7625_data {
u8 bridge_attached; u8 bridge_attached;
struct drm_connector *connector; struct drm_connector *connector;
struct mipi_dsi_device *dsi; struct mipi_dsi_device *dsi;
struct drm_dp_aux aux;
}; };
#endif /* __ANX7625_H__ */ #endif /* __ANX7625_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment