Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/bridge: tc358767: add IRQ and HPD support

Add support for interrupt and hotplug handling. Both are optional.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190528082747.3631-24-tomi.valkeinen@ti.com

authored by

Tomi Valkeinen and committed by
Andrzej Hajda
f25ee501 af9526f2

+145 -18
+145 -18
drivers/gpu/drm/bridge/tc358767.c
··· 71 71 72 72 /* System */ 73 73 #define TC_IDREG 0x0500 74 + #define SYSSTAT 0x0508 74 75 #define SYSCTRL 0x0510 75 76 #define DP0_AUDSRC_NO_INPUT (0 << 3) 76 77 #define DP0_AUDSRC_I2S_RX (1 << 3) ··· 80 79 #define DP0_VIDSRC_DPI_RX (2 << 0) 81 80 #define DP0_VIDSRC_COLOR_BAR (3 << 0) 82 81 #define GPIOM 0x0540 82 + #define GPIOC 0x0544 83 + #define GPIOO 0x0548 83 84 #define GPIOI 0x054c 84 85 #define INTCTL_G 0x0560 85 86 #define INTSTS_G 0x0564 87 + 88 + #define INT_SYSERR BIT(16) 89 + #define INT_GPIO_H(x) (1 << (x == 0 ? 2 : 10)) 90 + #define INT_GPIO_LC(x) (1 << (x == 0 ? 3 : 11)) 91 + 86 92 #define INT_GP0_LCNT 0x0584 87 93 #define INT_GP1_LCNT 0x0588 88 94 ··· 227 219 struct gpio_desc *sd_gpio; 228 220 struct gpio_desc *reset_gpio; 229 221 struct clk *refclk; 222 + 223 + /* do we have IRQ */ 224 + bool have_irq; 225 + 226 + /* HPD pin number (0 or 1) or -ENODEV */ 227 + int hpd_pin; 230 228 }; 231 229 232 230 static inline struct tc_data *aux_to_tc(struct drm_dp_aux *a) ··· 1123 1109 struct tc_data *tc = bridge_to_tc(bridge); 1124 1110 int ret; 1125 1111 1112 + ret = tc_get_display_props(tc); 1113 + if (ret < 0) { 1114 + dev_err(tc->dev, "failed to read display props: %d\n", ret); 1115 + return; 1116 + } 1117 + 1126 1118 ret = tc_main_link_enable(tc); 1127 1119 if (ret < 0) { 1128 1120 dev_err(tc->dev, "main link enable error: %d\n", ret); ··· 1241 1221 return count; 1242 1222 } 1243 1223 1244 - static void tc_connector_set_polling(struct tc_data *tc, 1245 - struct drm_connector *connector) 1246 - { 1247 - /* TODO: add support for HPD */ 1248 - connector->polled = DRM_CONNECTOR_POLL_CONNECT | 1249 - DRM_CONNECTOR_POLL_DISCONNECT; 1250 - } 1251 - 1252 1224 static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { 1253 1225 .get_modes = tc_connector_get_modes, 1254 1226 }; 1255 1227 1228 + static enum drm_connector_status tc_connector_detect(struct drm_connector *connector, 1229 + bool force) 1230 + { 1231 + struct tc_data *tc = connector_to_tc(connector); 1232 + bool conn; 1233 + u32 val; 1234 + int ret; 1235 + 1236 + if (tc->hpd_pin < 0) { 1237 + if (tc->panel) 1238 + return connector_status_connected; 1239 + else 1240 + return connector_status_unknown; 1241 + } 1242 + 1243 + tc_read(GPIOI, &val); 1244 + 1245 + conn = val & BIT(tc->hpd_pin); 1246 + 1247 + if (conn) 1248 + return connector_status_connected; 1249 + else 1250 + return connector_status_disconnected; 1251 + 1252 + err: 1253 + return connector_status_unknown; 1254 + } 1255 + 1256 1256 static const struct drm_connector_funcs tc_connector_funcs = { 1257 + .detect = tc_connector_detect, 1257 1258 .fill_modes = drm_helper_probe_single_connector_modes, 1258 1259 .destroy = drm_connector_cleanup, 1259 1260 .reset = drm_atomic_helper_connector_reset, ··· 1289 1248 struct drm_device *drm = bridge->dev; 1290 1249 int ret; 1291 1250 1292 - /* Create eDP connector */ 1251 + /* Create DP/eDP connector */ 1293 1252 drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs); 1294 1253 ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, 1295 1254 tc->panel ? DRM_MODE_CONNECTOR_eDP : 1296 1255 DRM_MODE_CONNECTOR_DisplayPort); 1297 1256 if (ret) 1298 1257 return ret; 1258 + 1259 + /* Don't poll if don't have HPD connected */ 1260 + if (tc->hpd_pin >= 0) { 1261 + if (tc->have_irq) 1262 + tc->connector.polled = DRM_CONNECTOR_POLL_HPD; 1263 + else 1264 + tc->connector.polled = DRM_CONNECTOR_POLL_CONNECT | 1265 + DRM_CONNECTOR_POLL_DISCONNECT; 1266 + } 1299 1267 1300 1268 if (tc->panel) 1301 1269 drm_panel_attach(tc->panel, &tc->connector); ··· 1372 1322 .val_format_endian = REGMAP_ENDIAN_LITTLE, 1373 1323 }; 1374 1324 1325 + static irqreturn_t tc_irq_handler(int irq, void *arg) 1326 + { 1327 + struct tc_data *tc = arg; 1328 + u32 val; 1329 + int r; 1330 + 1331 + r = regmap_read(tc->regmap, INTSTS_G, &val); 1332 + if (r) 1333 + return IRQ_NONE; 1334 + 1335 + if (!val) 1336 + return IRQ_NONE; 1337 + 1338 + if (val & INT_SYSERR) { 1339 + u32 stat = 0; 1340 + 1341 + regmap_read(tc->regmap, SYSSTAT, &stat); 1342 + 1343 + dev_err(tc->dev, "syserr %x\n", stat); 1344 + } 1345 + 1346 + if (tc->hpd_pin >= 0 && tc->bridge.dev) { 1347 + /* 1348 + * H is triggered when the GPIO goes high. 1349 + * 1350 + * LC is triggered when the GPIO goes low and stays low for 1351 + * the duration of LCNT 1352 + */ 1353 + bool h = val & INT_GPIO_H(tc->hpd_pin); 1354 + bool lc = val & INT_GPIO_LC(tc->hpd_pin); 1355 + 1356 + dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin, 1357 + h ? "H" : "", lc ? "LC" : ""); 1358 + 1359 + if (h || lc) 1360 + drm_kms_helper_hotplug_event(tc->bridge.dev); 1361 + } 1362 + 1363 + regmap_write(tc->regmap, INTSTS_G, val); 1364 + 1365 + return IRQ_HANDLED; 1366 + } 1367 + 1375 1368 static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) 1376 1369 { 1377 1370 struct device *dev = &client->dev; ··· 1466 1373 return ret; 1467 1374 } 1468 1375 1376 + ret = of_property_read_u32(dev->of_node, "toshiba,hpd-pin", 1377 + &tc->hpd_pin); 1378 + if (ret) { 1379 + tc->hpd_pin = -ENODEV; 1380 + } else { 1381 + if (tc->hpd_pin < 0 || tc->hpd_pin > 1) { 1382 + dev_err(dev, "failed to parse HPD number\n"); 1383 + return ret; 1384 + } 1385 + } 1386 + 1387 + if (client->irq > 0) { 1388 + /* enable SysErr */ 1389 + regmap_write(tc->regmap, INTCTL_G, INT_SYSERR); 1390 + 1391 + ret = devm_request_threaded_irq(dev, client->irq, 1392 + NULL, tc_irq_handler, 1393 + IRQF_ONESHOT, 1394 + "tc358767-irq", tc); 1395 + if (ret) { 1396 + dev_err(dev, "failed to register dp interrupt\n"); 1397 + return ret; 1398 + } 1399 + 1400 + tc->have_irq = true; 1401 + } 1402 + 1469 1403 ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev); 1470 1404 if (ret) { 1471 1405 dev_err(tc->dev, "can not read device ID: %d\n", ret); ··· 1506 1386 1507 1387 tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */ 1508 1388 1389 + if (tc->hpd_pin >= 0) { 1390 + u32 lcnt_reg = tc->hpd_pin == 0 ? INT_GP0_LCNT : INT_GP1_LCNT; 1391 + u32 h_lc = INT_GPIO_H(tc->hpd_pin) | INT_GPIO_LC(tc->hpd_pin); 1392 + 1393 + /* Set LCNT to 2ms */ 1394 + regmap_write(tc->regmap, lcnt_reg, 1395 + clk_get_rate(tc->refclk) * 2 / 1000); 1396 + /* We need the "alternate" mode for HPD */ 1397 + regmap_write(tc->regmap, GPIOM, BIT(tc->hpd_pin)); 1398 + 1399 + if (tc->have_irq) { 1400 + /* enable H & LC */ 1401 + regmap_update_bits(tc->regmap, INTCTL_G, h_lc, h_lc); 1402 + } 1403 + } 1404 + 1509 1405 ret = tc_aux_link_setup(tc); 1510 1406 if (ret) 1511 1407 return ret; ··· 1534 1398 if (ret) 1535 1399 return ret; 1536 1400 1537 - ret = tc_get_display_props(tc); 1538 - if (ret) 1539 - goto err_unregister_aux; 1540 - 1541 - tc_connector_set_polling(tc, &tc->connector); 1542 - 1543 1401 tc->bridge.funcs = &tc_bridge_funcs; 1544 1402 tc->bridge.of_node = dev->of_node; 1545 1403 drm_bridge_add(&tc->bridge); ··· 1541 1411 i2c_set_clientdata(client, tc); 1542 1412 1543 1413 return 0; 1544 - err_unregister_aux: 1545 - drm_dp_aux_unregister(&tc->aux); 1546 - return ret; 1547 1414 } 1548 1415 1549 1416 static int tc_remove(struct i2c_client *client)