17
17
18
18
#include <linux/clk.h>
19
19
#include <linux/delay.h>
20
+ #include <linux/extcon.h>
20
21
#include <linux/gpio/consumer.h>
21
22
#include <linux/i2c.h>
22
23
#include <linux/interrupt.h>
25
26
#include <linux/list.h>
26
27
#include <linux/module.h>
27
28
#include <linux/mutex.h>
29
+ #include <linux/of_graph.h>
28
30
#include <linux/regulator/consumer.h>
29
31
#include <linux/slab.h>
30
32
@@ -81,6 +83,10 @@ struct sii8620 {
81
83
struct edid * edid ;
82
84
unsigned int gen2_write_burst :1 ;
83
85
enum sii8620_mt_state mt_state ;
86
+ struct extcon_dev * extcon ;
87
+ struct notifier_block extcon_nb ;
88
+ struct work_struct extcon_wq ;
89
+ int cable_state ;
84
90
struct list_head mt_queue ;
85
91
struct {
86
92
int r_size ;
@@ -2170,6 +2176,77 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
2170
2176
ctx -> rc_dev = rc_dev ;
2171
2177
}
2172
2178
2179
+ static void sii8620_cable_out (struct sii8620 * ctx )
2180
+ {
2181
+ disable_irq (to_i2c_client (ctx -> dev )-> irq );
2182
+ sii8620_hw_off (ctx );
2183
+ }
2184
+
2185
+ static void sii8620_extcon_work (struct work_struct * work )
2186
+ {
2187
+ struct sii8620 * ctx =
2188
+ container_of (work , struct sii8620 , extcon_wq );
2189
+ int state = extcon_get_state (ctx -> extcon , EXTCON_DISP_MHL );
2190
+
2191
+ if (state == ctx -> cable_state )
2192
+ return ;
2193
+
2194
+ ctx -> cable_state = state ;
2195
+
2196
+ if (state > 0 )
2197
+ sii8620_cable_in (ctx );
2198
+ else
2199
+ sii8620_cable_out (ctx );
2200
+ }
2201
+
2202
+ static int sii8620_extcon_notifier (struct notifier_block * self ,
2203
+ unsigned long event , void * ptr )
2204
+ {
2205
+ struct sii8620 * ctx =
2206
+ container_of (self , struct sii8620 , extcon_nb );
2207
+
2208
+ schedule_work (& ctx -> extcon_wq );
2209
+
2210
+ return NOTIFY_DONE ;
2211
+ }
2212
+
2213
+ static int sii8620_extcon_init (struct sii8620 * ctx )
2214
+ {
2215
+ struct extcon_dev * edev ;
2216
+ struct device_node * musb , * muic ;
2217
+ int ret ;
2218
+
2219
+ /* get micro-USB connector node */
2220
+ musb = of_graph_get_remote_node (ctx -> dev -> of_node , 1 , -1 );
2221
+ /* next get micro-USB Interface Controller node */
2222
+ muic = of_get_next_parent (musb );
2223
+
2224
+ if (!muic ) {
2225
+ dev_info (ctx -> dev , "no extcon found, switching to 'always on' mode\n" );
2226
+ return 0 ;
2227
+ }
2228
+
2229
+ edev = extcon_find_edev_by_node (muic );
2230
+ of_node_put (muic );
2231
+ if (IS_ERR (edev )) {
2232
+ if (PTR_ERR (edev ) == - EPROBE_DEFER )
2233
+ return - EPROBE_DEFER ;
2234
+ dev_err (ctx -> dev , "Invalid or missing extcon\n" );
2235
+ return PTR_ERR (edev );
2236
+ }
2237
+
2238
+ ctx -> extcon = edev ;
2239
+ ctx -> extcon_nb .notifier_call = sii8620_extcon_notifier ;
2240
+ INIT_WORK (& ctx -> extcon_wq , sii8620_extcon_work );
2241
+ ret = extcon_register_notifier (edev , EXTCON_DISP_MHL , & ctx -> extcon_nb );
2242
+ if (ret ) {
2243
+ dev_err (ctx -> dev , "failed to register notifier for MHL\n" );
2244
+ return ret ;
2245
+ }
2246
+
2247
+ return 0 ;
2248
+ }
2249
+
2173
2250
static inline struct sii8620 * bridge_to_sii8620 (struct drm_bridge * bridge )
2174
2251
{
2175
2252
return container_of (bridge , struct sii8620 , bridge );
@@ -2302,13 +2379,20 @@ static int sii8620_probe(struct i2c_client *client,
2302
2379
if (ret )
2303
2380
return ret ;
2304
2381
2382
+ ret = sii8620_extcon_init (ctx );
2383
+ if (ret < 0 ) {
2384
+ dev_err (ctx -> dev , "failed to initialize EXTCON\n" );
2385
+ return ret ;
2386
+ }
2387
+
2305
2388
i2c_set_clientdata (client , ctx );
2306
2389
2307
2390
ctx -> bridge .funcs = & sii8620_bridge_funcs ;
2308
2391
ctx -> bridge .of_node = dev -> of_node ;
2309
2392
drm_bridge_add (& ctx -> bridge );
2310
2393
2311
- sii8620_cable_in (ctx );
2394
+ if (!ctx -> extcon )
2395
+ sii8620_cable_in (ctx );
2312
2396
2313
2397
return 0 ;
2314
2398
}
@@ -2317,8 +2401,15 @@ static int sii8620_remove(struct i2c_client *client)
2317
2401
{
2318
2402
struct sii8620 * ctx = i2c_get_clientdata (client );
2319
2403
2320
- disable_irq (to_i2c_client (ctx -> dev )-> irq );
2321
- sii8620_hw_off (ctx );
2404
+ if (ctx -> extcon ) {
2405
+ extcon_unregister_notifier (ctx -> extcon , EXTCON_DISP_MHL ,
2406
+ & ctx -> extcon_nb );
2407
+ flush_work (& ctx -> extcon_wq );
2408
+ if (ctx -> cable_state > 0 )
2409
+ sii8620_cable_out (ctx );
2410
+ } else {
2411
+ sii8620_cable_out (ctx );
2412
+ }
2322
2413
drm_bridge_remove (& ctx -> bridge );
2323
2414
2324
2415
return 0 ;
0 commit comments