From c5cdb7fa26a8aabcafc965a69a6c2cc81977fc09 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Thu, 18 Aug 2016 10:27:19 +0900 Subject: [PATCH] drm: bridge: sii8620: add "extcon" support sii8620 is always activated whether MHL device is connected or not. To dynamically turn on/off sii8620 as following the state of device connection, this patch adds extcon-related features to the driver. Change-Id: I59b6083beb3bb27a0e3f1bc8efacd99123874a7a Signed-off-by: Dongwoo Lee --- drivers/gpu/drm/bridge/sii8620.c | 97 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/sii8620.c b/drivers/gpu/drm/bridge/sii8620.c index 1bd9cf3..90d93af 100644 --- a/drivers/gpu/drm/bridge/sii8620.c +++ b/drivers/gpu/drm/bridge/sii8620.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "sii8620.h" @@ -67,6 +68,12 @@ struct sii8620 { unsigned int mt_done:1; int irq; struct list_head mt_queue; + + /* extcon features */ + struct work_struct work; + struct notifier_block mhl_nb; + struct extcon_specific_cable_nb mhl_cable_nb; + struct extcon_dev *edev; }; struct sii8620_msc_msg; @@ -615,6 +622,13 @@ static int sii8620_hw_on(struct sii8620 *ctx) return 0; } +static int sii8620_hw_off(struct sii8620 *ctx) +{ + gpiod_set_value(ctx->gpio_reset, 0); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + static void sii8620_hw_reset(struct sii8620 *ctx) { usleep_range(10000, 20000); @@ -1424,6 +1438,13 @@ static void sii8620_cable_in(struct sii8620 *ctx) enable_irq(ctx->irq); } +static void sii8620_cable_out(struct sii8620 *ctx) +{ + disable_irq(ctx->irq); + clk_disable_unprepare(ctx->clk_xtal); + sii8620_hw_off(ctx); +} + static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) { return container_of(bridge, struct sii8620, bridge); @@ -1458,6 +1479,75 @@ out: return ret; } +static void sii8620_mhl_worker(struct work_struct *work) +{ + struct sii8620 *ctx = container_of(work, struct sii8620, work); + + if (extcon_get_cable_state(ctx->edev, "MHL")) + sii8620_cable_in(ctx); + else + sii8620_cable_out(ctx); +} + +static int sii8620_mhl_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct sii8620 *ctx = container_of(nb, struct sii8620, mhl_nb); + + schedule_work(&ctx->work); + + return 0; +} + +static int sii8620_extcon_activate(struct sii8620 *ctx) +{ + struct device_node *np = ctx->dev->of_node; + struct extcon_dev *edev; + int ret; + + if (of_property_read_bool(np, "extcon")) { + INIT_WORK(&ctx->work, sii8620_mhl_worker); + + ctx->mhl_nb.notifier_call = sii8620_mhl_notifier; + + edev = extcon_get_edev_by_phandle(ctx->dev, 0); + if (IS_ERR(edev)) { + dev_dbg(ctx->dev, "Cannot get extcon device\n"); + return -EPROBE_DEFER; + } + + ret = extcon_register_interest(&ctx->mhl_cable_nb, edev->name, + "MHL", &ctx->mhl_nb); + if (ret < 0) { + dev_err(ctx->dev, "Cannot register MHL notifier\n"); + return ret; + } + + ctx->edev = edev; + } else { + dev_dbg(ctx->dev, "extcon for MHL is not supported\n"); + sii8620_cable_in(ctx); + } + + return 0; +} + +static void sii8620_extcon_deactivate(struct sii8620 *ctx) +{ + struct device_node *np = ctx->dev->of_node; + + if (of_property_read_bool(np, "extcon")) { + extcon_unregister_interest(&ctx->mhl_cable_nb); + /* + * When the driver is being removed, it needs to be powered + * off if still connected. + */ + if (extcon_get_cable_state(ctx->edev, "MHL")) + sii8620_cable_out(ctx); + } else { + sii8620_cable_out(ctx); + } +} static const struct drm_bridge_funcs sii8620_bridge_funcs = { .pre_enable = sii8620_bridge_dummy, @@ -1515,9 +1605,11 @@ static int sii8620_probe(struct i2c_client *client, if (ret) return ret; - i2c_set_clientdata(client, ctx); + ret = sii8620_extcon_activate(ctx); + if (ret) + return ret; - sii8620_cable_in(ctx); + i2c_set_clientdata(client, ctx); ctx->bridge.funcs = &sii8620_bridge_funcs; ctx->bridge.of_node = dev->of_node; @@ -1531,6 +1623,7 @@ static int sii8620_remove(struct i2c_client *client) struct sii8620 *ctx = i2c_get_clientdata(client); drm_bridge_remove(&ctx->bridge); + sii8620_extcon_deactivate(ctx); return 0; } -- 2.7.4