Skip to content

Commit

Permalink
drm/bridge/sii8620: use micro-USB cable detection logic to detect MHL
Browse files Browse the repository at this point in the history
Currently MHL chip must be turned on permanently to detect MHL cable. It
duplicates micro-USB controller's (MUIC) functionality and consumes
unnecessary power. Lets use extcon attached to MUIC to enable MHL chip
only if it detects MHL cable.

Signed-off-by: Maciej Purski <m.purski@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Andrzej Hajda <a.hajda@samsung.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
  • Loading branch information
Maciej Purski authored and Chanwoo Choi committed Mar 12, 2018
1 parent 370ed7a commit 6888384
Showing 1 changed file with 94 additions and 3 deletions.
97 changes: 94 additions & 3 deletions drivers/gpu/drm/bridge/sil-sii8620.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
Expand All @@ -25,6 +26,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

Expand Down Expand Up @@ -81,6 +83,10 @@ struct sii8620 {
struct edid *edid;
unsigned int gen2_write_burst:1;
enum sii8620_mt_state mt_state;
struct extcon_dev *extcon;
struct notifier_block extcon_nb;
struct work_struct extcon_wq;
int cable_state;
struct list_head mt_queue;
struct {
int r_size;
Expand Down Expand Up @@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
ctx->rc_dev = rc_dev;
}

static void sii8620_cable_out(struct sii8620 *ctx)
{
disable_irq(to_i2c_client(ctx->dev)->irq);
sii8620_hw_off(ctx);
}

static void sii8620_extcon_work(struct work_struct *work)
{
struct sii8620 *ctx =
container_of(work, struct sii8620, extcon_wq);
int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL);

if (state == ctx->cable_state)
return;

ctx->cable_state = state;

if (state > 0)
sii8620_cable_in(ctx);
else
sii8620_cable_out(ctx);
}

static int sii8620_extcon_notifier(struct notifier_block *self,
unsigned long event, void *ptr)
{
struct sii8620 *ctx =
container_of(self, struct sii8620, extcon_nb);

schedule_work(&ctx->extcon_wq);

return NOTIFY_DONE;
}

static int sii8620_extcon_init(struct sii8620 *ctx)
{
struct extcon_dev *edev;
struct device_node *musb, *muic;
int ret;

/* get micro-USB connector node */
musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1);
/* next get micro-USB Interface Controller node */
muic = of_get_next_parent(musb);

if (!muic) {
dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n");
return 0;
}

edev = extcon_find_edev_by_node(muic);
of_node_put(muic);
if (IS_ERR(edev)) {
if (PTR_ERR(edev) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(ctx->dev, "Invalid or missing extcon\n");
return PTR_ERR(edev);
}

ctx->extcon = edev;
ctx->extcon_nb.notifier_call = sii8620_extcon_notifier;
INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work);
ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb);
if (ret) {
dev_err(ctx->dev, "failed to register notifier for MHL\n");
return ret;
}

return 0;
}

static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
{
return container_of(bridge, struct sii8620, bridge);
Expand Down Expand Up @@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client,
if (ret)
return ret;

ret = sii8620_extcon_init(ctx);
if (ret < 0) {
dev_err(ctx->dev, "failed to initialize EXTCON\n");
return ret;
}

i2c_set_clientdata(client, ctx);

ctx->bridge.funcs = &sii8620_bridge_funcs;
ctx->bridge.of_node = dev->of_node;
drm_bridge_add(&ctx->bridge);

sii8620_cable_in(ctx);
if (!ctx->extcon)
sii8620_cable_in(ctx);

return 0;
}
Expand All @@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client)
{
struct sii8620 *ctx = i2c_get_clientdata(client);

disable_irq(to_i2c_client(ctx->dev)->irq);
sii8620_hw_off(ctx);
if (ctx->extcon) {
extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL,
&ctx->extcon_nb);
flush_work(&ctx->extcon_wq);
if (ctx->cable_state > 0)
sii8620_cable_out(ctx);
} else {
sii8620_cable_out(ctx);
}
drm_bridge_remove(&ctx->bridge);

return 0;
Expand Down

0 comments on commit 6888384

Please sign in to comment.