Skip to content

Commit

Permalink
drm/bridge: anx7625: Support reading edid through aux channel
Browse files Browse the repository at this point in the history
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>
Reviewed-by: Xin Ji <xji@analogixsemi.com>
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Robert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220213103437.3363848-3-hsinyi@chromium.org
  • Loading branch information
Hsin-Yi Wang authored and Robert Foss committed Feb 14, 2022
1 parent 57bfb34 commit adca62e
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 18 deletions.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/bridge/analogix/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ config DRM_ANALOGIX_ANX7625
tristate "Analogix Anx7625 MIPI to DP interface support"
depends on DRM
depends on OF
select DRM_DP_AUX_BUS
select DRM_DP_HELPER
select DRM_MIPI_DSI
help
ANX7625 is an ultra-low power 4K mobile HD transmitter
Expand Down
120 changes: 102 additions & 18 deletions drivers/gpu/drm/bridge/analogix/anx7625.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc_helper.h>
#include <drm/dp/drm_dp_aux_bus.h>
#include <drm/dp/drm_dp_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_hdcp.h>
Expand Down Expand Up @@ -231,19 +232,23 @@ 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;
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");
return -EINVAL;
}

if (!len)
return len;

addrl = address & 0xFF;
addrm = (address >> 8) & 0xFF;
addrh = (address >> 16) & 0xFF;
Expand All @@ -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,
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,
AP_AUX_BUFF_START, len, buf);
/* Enable aux access */
Expand All @@ -275,14 +280,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 == DP_AUX_NATIVE_WRITE)
return 0;
if (is_write)
return len;

/* Read done, read out dpcd data */
ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
Expand All @@ -292,7 +297,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,
Expand Down Expand Up @@ -867,7 +872,7 @@ static int anx7625_hdcp_enable(struct anx7625_data *ctx)
}

/* 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)) {
pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap);
return 0;
Expand Down Expand Up @@ -956,7 +961,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, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data);
ret |= anx7625_aux_trans(ctx, DP_AUX_NATIVE_WRITE, 0x000600, 1, &data);
if (ret < 0)
DRM_DEV_ERROR(dev, "IO error : mute video fail\n");

Expand Down Expand Up @@ -1655,11 +1660,56 @@ 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;
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)
{
struct device *dev = &ctx->client->dev;
Expand Down Expand Up @@ -2066,6 +2116,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,
Expand All @@ -2079,6 +2136,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,
Expand Down Expand Up @@ -2344,6 +2408,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,
Expand Down Expand Up @@ -2501,6 +2566,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)
{
Expand All @@ -2523,13 +2594,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);

Expand Down Expand Up @@ -2577,13 +2641,32 @@ 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");
goto free_wq;
}

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);
Expand All @@ -2596,7 +2679,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;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/bridge/analogix/anx7625.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ struct anx7625_data {
u8 bridge_attached;
struct drm_connector *connector;
struct mipi_dsi_device *dsi;
struct drm_dp_aux aux;
};

#endif /* __ANX7625_H__ */

0 comments on commit adca62e

Please sign in to comment.