Skip to content

Commit bf73537

Browse files
committed
drm/bridge: ti-sn65dsi86: Break GPIO and MIPI-to-eDP bridge into sub-drivers
Let's use the newly minted aux bus to break up the driver into sub drivers. We're not doing a full breakup here: all the code is still in the same file and remains largely untouched. The big goal here of using sub-drivers is to allow part of our code to finish probing even if some other code needs to defer. This can solve some chicken-and-egg problems. Specifically: - In commit 48834e6 ("drm/panel-simple: Support hpd-gpios for delaying prepare()") we had to add a bit of a hack to simpel-panel to support HPD showing up late. We can get rid of that hack now since the GPIO part of our driver can finish probing early. - We have a desire to expose our DDC bus to simple-panel (and perhaps to a backlight driver?). That will end up with the same chicken-and-egg problem. A future patch to move this to a sub-driver will fix it. - If/when we support the PWM functionality present in the bridge chip for a backlight we'll end up with another chicken-and-egg problem. If we allow the PWM to be a sub-driver too then it solves this problem. Signed-off-by: Douglas Anderson <[email protected]> Acked-by: Linus Walleij <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/20210423095743.v5.9.I3e68fa38c4ccbdbdf145cad2b01e83a1e5eac302@changeid
1 parent bef236a commit bf73537

File tree

2 files changed

+200
-53
lines changed

2 files changed

+200
-53
lines changed

drivers/gpu/drm/bridge/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ config DRM_TI_SN65DSI86
278278
select REGMAP_I2C
279279
select DRM_PANEL
280280
select DRM_MIPI_DSI
281+
select AUXILIARY_BUS
281282
help
282283
Texas Instruments SN65DSI86 DSI to eDP Bridge driver
283284

drivers/gpu/drm/bridge/ti-sn65dsi86.c

Lines changed: 199 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* datasheet: https://www.ti.com/lit/ds/symlink/sn65dsi86.pdf
55
*/
66

7+
#include <linux/auxiliary_bus.h>
78
#include <linux/bits.h>
89
#include <linux/clk.h>
910
#include <linux/debugfs.h>
@@ -113,7 +114,10 @@
113114

114115
/**
115116
* struct ti_sn65dsi86 - Platform data for ti-sn65dsi86 driver.
116-
* @dev: Pointer to our device.
117+
* @bridge_aux: AUX-bus sub device for MIPI-to-eDP bridge functionality.
118+
* @gpio_aux: AUX-bus sub device for GPIO controller functionality.
119+
*
120+
* @dev: Pointer to the top level (i2c) device.
117121
* @regmap: Regmap for accessing i2c.
118122
* @aux: Our aux channel.
119123
* @bridge: Our bridge.
@@ -140,6 +144,9 @@
140144
* each other's read-modify-write.
141145
*/
142146
struct ti_sn65dsi86 {
147+
struct auxiliary_device bridge_aux;
148+
struct auxiliary_device gpio_aux;
149+
143150
struct device *dev;
144151
struct regmap *regmap;
145152
struct drm_dp_aux aux;
@@ -1137,8 +1144,10 @@ static const char * const ti_sn_bridge_gpio_names[SN_NUM_GPIOS] = {
11371144
"GPIO1", "GPIO2", "GPIO3", "GPIO4"
11381145
};
11391146

1140-
static int ti_sn_setup_gpio_controller(struct ti_sn65dsi86 *pdata)
1147+
static int ti_sn_gpio_probe(struct auxiliary_device *adev,
1148+
const struct auxiliary_device_id *id)
11411149
{
1150+
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
11421151
int ret;
11431152

11441153
/* Only init if someone is going to use us as a GPIO controller */
@@ -1160,20 +1169,41 @@ static int ti_sn_setup_gpio_controller(struct ti_sn65dsi86 *pdata)
11601169
pdata->gchip.names = ti_sn_bridge_gpio_names;
11611170
pdata->gchip.ngpio = SN_NUM_GPIOS;
11621171
pdata->gchip.base = -1;
1163-
ret = devm_gpiochip_add_data(pdata->dev, &pdata->gchip, pdata);
1172+
ret = devm_gpiochip_add_data(&adev->dev, &pdata->gchip, pdata);
11641173
if (ret)
11651174
dev_err(pdata->dev, "can't add gpio chip\n");
11661175

11671176
return ret;
11681177
}
11691178

1170-
#else
1179+
static const struct auxiliary_device_id ti_sn_gpio_id_table[] = {
1180+
{ .name = "ti_sn65dsi86.gpio", },
1181+
{},
1182+
};
11711183

1172-
static inline int ti_sn_setup_gpio_controller(struct ti_sn65dsi86 *pdata)
1184+
MODULE_DEVICE_TABLE(auxiliary, ti_sn_gpio_id_table);
1185+
1186+
static struct auxiliary_driver ti_sn_gpio_driver = {
1187+
.name = "gpio",
1188+
.probe = ti_sn_gpio_probe,
1189+
.id_table = ti_sn_gpio_id_table,
1190+
};
1191+
1192+
static int __init ti_sn_gpio_register(void)
11731193
{
1174-
return 0;
1194+
return auxiliary_driver_register(&ti_sn_gpio_driver);
11751195
}
11761196

1197+
static void __exit ti_sn_gpio_unregister(void)
1198+
{
1199+
auxiliary_driver_unregister(&ti_sn_gpio_driver);
1200+
}
1201+
1202+
#else
1203+
1204+
static inline int ti_sn_gpio_register(void) { return 0; }
1205+
static inline void ti_sn_gpio_unregister(void) {}
1206+
11771207
#endif
11781208

11791209
static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata,
@@ -1225,11 +1255,124 @@ static void ti_sn_bridge_parse_lanes(struct ti_sn65dsi86 *pdata,
12251255
pdata->ln_polrs = ln_polrs;
12261256
}
12271257

1258+
static int ti_sn_bridge_probe(struct auxiliary_device *adev,
1259+
const struct auxiliary_device_id *id)
1260+
{
1261+
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
1262+
struct device_node *np = pdata->dev->of_node;
1263+
int ret;
1264+
1265+
ret = drm_of_find_panel_or_bridge(np, 1, 0, &pdata->panel, NULL);
1266+
if (ret) {
1267+
DRM_ERROR("could not find any panel node\n");
1268+
return ret;
1269+
}
1270+
1271+
ti_sn_bridge_parse_lanes(pdata, np);
1272+
1273+
ret = ti_sn_bridge_parse_dsi_host(pdata);
1274+
if (ret)
1275+
return ret;
1276+
1277+
pdata->aux.name = "ti-sn65dsi86-aux";
1278+
pdata->aux.dev = pdata->dev;
1279+
pdata->aux.transfer = ti_sn_aux_transfer;
1280+
drm_dp_aux_init(&pdata->aux);
1281+
1282+
pdata->bridge.funcs = &ti_sn_bridge_funcs;
1283+
pdata->bridge.of_node = np;
1284+
1285+
drm_bridge_add(&pdata->bridge);
1286+
1287+
return 0;
1288+
}
1289+
1290+
static void ti_sn_bridge_remove(struct auxiliary_device *adev)
1291+
{
1292+
struct ti_sn65dsi86 *pdata = dev_get_drvdata(adev->dev.parent);
1293+
1294+
if (!pdata)
1295+
return;
1296+
1297+
if (pdata->dsi) {
1298+
mipi_dsi_detach(pdata->dsi);
1299+
mipi_dsi_device_unregister(pdata->dsi);
1300+
}
1301+
1302+
kfree(pdata->edid);
1303+
1304+
drm_bridge_remove(&pdata->bridge);
1305+
1306+
of_node_put(pdata->host_node);
1307+
}
1308+
1309+
static const struct auxiliary_device_id ti_sn_bridge_id_table[] = {
1310+
{ .name = "ti_sn65dsi86.bridge", },
1311+
{},
1312+
};
1313+
1314+
static struct auxiliary_driver ti_sn_bridge_driver = {
1315+
.name = "bridge",
1316+
.probe = ti_sn_bridge_probe,
1317+
.remove = ti_sn_bridge_remove,
1318+
.id_table = ti_sn_bridge_id_table,
1319+
};
1320+
12281321
static void ti_sn65dsi86_runtime_disable(void *data)
12291322
{
12301323
pm_runtime_disable(data);
12311324
}
12321325

1326+
static void ti_sn65dsi86_uninit_aux(void *data)
1327+
{
1328+
auxiliary_device_uninit(data);
1329+
}
1330+
1331+
static void ti_sn65dsi86_delete_aux(void *data)
1332+
{
1333+
auxiliary_device_delete(data);
1334+
}
1335+
1336+
/*
1337+
* AUX bus docs say that a non-NULL release is mandatory, but it makes no
1338+
* sense for the model used here where all of the aux devices are allocated
1339+
* in the single shared structure. We'll use this noop as a workaround.
1340+
*/
1341+
static void ti_sn65dsi86_noop(struct device *dev) {}
1342+
1343+
static int ti_sn65dsi86_add_aux_device(struct ti_sn65dsi86 *pdata,
1344+
struct auxiliary_device *aux,
1345+
const char *name)
1346+
{
1347+
struct device *dev = pdata->dev;
1348+
int ret;
1349+
1350+
/*
1351+
* NOTE: It would be nice to set the "of_node" of our children to be
1352+
* the same "of_node"" that the top-level component has. That doesn't
1353+
* work, though, since pinctrl will try (and fail) to reserve the
1354+
* pins again. Until that gets sorted out the children will just need
1355+
* to look at the of_node of the main device.
1356+
*/
1357+
1358+
aux->name = name;
1359+
aux->dev.parent = dev;
1360+
aux->dev.release = ti_sn65dsi86_noop;
1361+
ret = auxiliary_device_init(aux);
1362+
if (ret)
1363+
return ret;
1364+
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_uninit_aux, aux);
1365+
if (ret)
1366+
return ret;
1367+
1368+
ret = auxiliary_device_add(aux);
1369+
if (ret)
1370+
return ret;
1371+
ret = devm_add_action_or_reset(dev, ti_sn65dsi86_delete_aux, aux);
1372+
1373+
return ret;
1374+
}
1375+
12331376
static int ti_sn65dsi86_probe(struct i2c_client *client,
12341377
const struct i2c_device_id *id)
12351378
{
@@ -1279,54 +1422,24 @@ static int ti_sn65dsi86_probe(struct i2c_client *client,
12791422

12801423
ti_sn65dsi86_debugfs_init(pdata);
12811424

1282-
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &pdata->panel, NULL);
1283-
if (ret) {
1284-
DRM_ERROR("could not find any panel node\n");
1285-
return ret;
1286-
}
1287-
1288-
ti_sn_bridge_parse_lanes(pdata, dev->of_node);
1289-
1290-
ret = ti_sn_bridge_parse_dsi_host(pdata);
1291-
if (ret)
1292-
return ret;
1293-
1294-
ret = ti_sn_setup_gpio_controller(pdata);
1295-
if (ret)
1296-
return ret;
1297-
1298-
pdata->aux.name = "ti-sn65dsi86-aux";
1299-
pdata->aux.dev = dev;
1300-
pdata->aux.transfer = ti_sn_aux_transfer;
1301-
drm_dp_aux_init(&pdata->aux);
1302-
1303-
pdata->bridge.funcs = &ti_sn_bridge_funcs;
1304-
pdata->bridge.of_node = dev->of_node;
1305-
1306-
drm_bridge_add(&pdata->bridge);
1307-
1308-
return 0;
1309-
}
1310-
1311-
static int ti_sn65dsi86_remove(struct i2c_client *client)
1312-
{
1313-
struct ti_sn65dsi86 *pdata = i2c_get_clientdata(client);
1314-
1315-
if (!pdata)
1316-
return -EINVAL;
1425+
/*
1426+
* Break ourselves up into a collection of aux devices. The only real
1427+
* motiviation here is to solve the chicken-and-egg problem of probe
1428+
* ordering. The bridge wants the panel to be there when it probes.
1429+
* The panel wants its HPD GPIO (provided by sn65dsi86 on some boards)
1430+
* when it probes. There will soon be other devices (DDC I2C bus, PWM)
1431+
* that have the same problem. Having sub-devices allows the some sub
1432+
* devices to finish probing even if others return -EPROBE_DEFER and
1433+
* gets us around the problems.
1434+
*/
13171435

1318-
if (pdata->dsi) {
1319-
mipi_dsi_detach(pdata->dsi);
1320-
mipi_dsi_device_unregister(pdata->dsi);
1436+
if (IS_ENABLED(CONFIG_OF_GPIO)) {
1437+
ret = ti_sn65dsi86_add_aux_device(pdata, &pdata->gpio_aux, "gpio");
1438+
if (ret)
1439+
return ret;
13211440
}
13221441

1323-
kfree(pdata->edid);
1324-
1325-
drm_bridge_remove(&pdata->bridge);
1326-
1327-
of_node_put(pdata->host_node);
1328-
1329-
return 0;
1442+
return ti_sn65dsi86_add_aux_device(pdata, &pdata->bridge_aux, "bridge");
13301443
}
13311444

13321445
static struct i2c_device_id ti_sn65dsi86_id[] = {
@@ -1348,10 +1461,43 @@ static struct i2c_driver ti_sn65dsi86_driver = {
13481461
.pm = &ti_sn65dsi86_pm_ops,
13491462
},
13501463
.probe = ti_sn65dsi86_probe,
1351-
.remove = ti_sn65dsi86_remove,
13521464
.id_table = ti_sn65dsi86_id,
13531465
};
1354-
module_i2c_driver(ti_sn65dsi86_driver);
1466+
1467+
static int __init ti_sn65dsi86_init(void)
1468+
{
1469+
int ret;
1470+
1471+
ret = i2c_add_driver(&ti_sn65dsi86_driver);
1472+
if (ret)
1473+
return ret;
1474+
1475+
ret = ti_sn_gpio_register();
1476+
if (ret)
1477+
goto err_main_was_registered;
1478+
1479+
ret = auxiliary_driver_register(&ti_sn_bridge_driver);
1480+
if (ret)
1481+
goto err_gpio_was_registered;
1482+
1483+
return 0;
1484+
1485+
err_gpio_was_registered:
1486+
ti_sn_gpio_unregister();
1487+
err_main_was_registered:
1488+
i2c_del_driver(&ti_sn65dsi86_driver);
1489+
1490+
return ret;
1491+
}
1492+
module_init(ti_sn65dsi86_init);
1493+
1494+
static void __exit ti_sn65dsi86_exit(void)
1495+
{
1496+
auxiliary_driver_unregister(&ti_sn_bridge_driver);
1497+
ti_sn_gpio_unregister();
1498+
i2c_del_driver(&ti_sn65dsi86_driver);
1499+
}
1500+
module_exit(ti_sn65dsi86_exit);
13551501

13561502
MODULE_AUTHOR("Sandeep Panda <[email protected]>");
13571503
MODULE_DESCRIPTION("sn65dsi86 DSI to eDP bridge driver");

0 commit comments

Comments
 (0)