Skip to content

Commit 826cff3

Browse files
Philip Chendianders
Philip Chen
authored andcommitted
drm/bridge: parade-ps8640: Enable runtime power management
Fit ps8640 driver into runtime power management framework: First, break _poweron() to 3 parts: (1) turn on power and wait for ps8640's internal MCU to finish init (2) check panel HPD (which is proxied by GPIO9) (3) the other configs. As runtime_resume() can be called before panel is powered, we only add (1) to _resume() and leave (2)(3) to _pre_enable(). We also add (2) to _aux_transfer() as we want to ensure panel HPD is asserted before we start AUX CH transactions. Second, the original driver has a mysterious delay of 50 ms between (2) and (3). Since Parade's support can't explain what the delay is for, and we don't see removing the delay break any boards at hand, remove the delay to fit into this driver change. In addition, rename "powered" to "pre_enabled" and don't check for it in the pm_runtime calls. The pm_runtime calls are already refcounted so there's no reason to check there. The other user of "powered", _get_edid(), only cares if pre_enable() has already been called. Lastly, change some existing DRM_...() logging to dev_...() along the way, since DRM_...() seem to be deprecated in [1]. [1] https://patchwork.freedesktop.org/patch/454760/ Signed-off-by: Philip Chen <[email protected]> Reviewed-by: Douglas Anderson <[email protected]> Reviewed-by: Stephen Boyd <[email protected]> [dianders: fixed whitespace warning reported by dim tool] Signed-off-by: Douglas Anderson <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/20211028105754.v5.1.I828f5db745535fb7e36e8ffdd62d546f6d08b6d1@changeid
1 parent 84e9dfd commit 826cff3

File tree

1 file changed

+119
-71
lines changed

1 file changed

+119
-71
lines changed

drivers/gpu/drm/bridge/parade-ps8640.c

Lines changed: 119 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/i2c.h>
1010
#include <linux/module.h>
1111
#include <linux/of_graph.h>
12+
#include <linux/pm_runtime.h>
1213
#include <linux/regmap.h>
1314
#include <linux/regulator/consumer.h>
1415

@@ -100,7 +101,7 @@ struct ps8640 {
100101
struct regulator_bulk_data supplies[2];
101102
struct gpio_desc *gpio_reset;
102103
struct gpio_desc *gpio_powerdown;
103-
bool powered;
104+
bool pre_enabled;
104105
};
105106

106107
static const struct regmap_config ps8640_regmap_config[] = {
@@ -148,8 +149,29 @@ static inline struct ps8640 *aux_to_ps8640(struct drm_dp_aux *aux)
148149
return container_of(aux, struct ps8640, aux);
149150
}
150151

151-
static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux,
152-
struct drm_dp_aux_msg *msg)
152+
static int ps8640_ensure_hpd(struct ps8640 *ps_bridge)
153+
{
154+
struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL];
155+
struct device *dev = &ps_bridge->page[PAGE2_TOP_CNTL]->dev;
156+
int status;
157+
int ret;
158+
159+
/*
160+
* Apparently something about the firmware in the chip signals that
161+
* HPD goes high by reporting GPIO9 as high (even though HPD isn't
162+
* actually connected to GPIO9).
163+
*/
164+
ret = regmap_read_poll_timeout(map, PAGE2_GPIO_H, status,
165+
status & PS_GPIO9, 20 * 1000, 200 * 1000);
166+
167+
if (ret < 0)
168+
dev_warn(dev, "HPD didn't go high: %d\n", ret);
169+
170+
return ret;
171+
}
172+
173+
static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
174+
struct drm_dp_aux_msg *msg)
153175
{
154176
struct ps8640 *ps_bridge = aux_to_ps8640(aux);
155177
struct regmap *map = ps_bridge->regmap[PAGE0_DP_CNTL];
@@ -274,38 +296,49 @@ static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux,
274296
return len;
275297
}
276298

277-
static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
278-
const enum ps8640_vdo_control ctrl)
299+
static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux,
300+
struct drm_dp_aux_msg *msg)
301+
{
302+
struct ps8640 *ps_bridge = aux_to_ps8640(aux);
303+
struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev;
304+
int ret;
305+
306+
pm_runtime_get_sync(dev);
307+
ret = ps8640_ensure_hpd(ps_bridge);
308+
if (!ret)
309+
ret = ps8640_aux_transfer_msg(aux, msg);
310+
pm_runtime_mark_last_busy(dev);
311+
pm_runtime_put_autosuspend(dev);
312+
313+
return ret;
314+
}
315+
316+
static void ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
317+
const enum ps8640_vdo_control ctrl)
279318
{
280319
struct regmap *map = ps_bridge->regmap[PAGE3_DSI_CNTL1];
320+
struct device *dev = &ps_bridge->page[PAGE3_DSI_CNTL1]->dev;
281321
u8 vdo_ctrl_buf[] = { VDO_CTL_ADD, ctrl };
282322
int ret;
283323

284324
ret = regmap_bulk_write(map, PAGE3_SET_ADD,
285325
vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
286326

287-
if (ret < 0) {
288-
DRM_ERROR("failed to %sable VDO: %d\n",
289-
ctrl == ENABLE ? "en" : "dis", ret);
290-
return ret;
291-
}
292-
293-
return 0;
327+
if (ret < 0)
328+
dev_err(dev, "failed to %sable VDO: %d\n",
329+
ctrl == ENABLE ? "en" : "dis", ret);
294330
}
295331

296-
static void ps8640_bridge_poweron(struct ps8640 *ps_bridge)
332+
static int __maybe_unused ps8640_resume(struct device *dev)
297333
{
298-
struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL];
299-
int ret, status;
300-
301-
if (ps_bridge->powered)
302-
return;
334+
struct ps8640 *ps_bridge = dev_get_drvdata(dev);
335+
int ret;
303336

304337
ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
305338
ps_bridge->supplies);
306339
if (ret < 0) {
307-
DRM_ERROR("cannot enable regulators %d\n", ret);
308-
return;
340+
dev_err(dev, "cannot enable regulators %d\n", ret);
341+
return ret;
309342
}
310343

311344
gpiod_set_value(ps_bridge->gpio_powerdown, 0);
@@ -314,86 +347,78 @@ static void ps8640_bridge_poweron(struct ps8640 *ps_bridge)
314347
gpiod_set_value(ps_bridge->gpio_reset, 0);
315348

316349
/*
317-
* Wait for the ps8640 embedded MCU to be ready
318-
* First wait 200ms and then check the MCU ready flag every 20ms
350+
* Mystery 200 ms delay for the "MCU to be ready". It's unclear if
351+
* this is truly necessary since the MCU will already signal that
352+
* things are "good to go" by signaling HPD on "gpio 9". See
353+
* ps8640_ensure_hpd(). For now we'll keep this mystery delay just in
354+
* case.
319355
*/
320356
msleep(200);
321357

322-
ret = regmap_read_poll_timeout(map, PAGE2_GPIO_H, status,
323-
status & PS_GPIO9, 20 * 1000, 200 * 1000);
324-
325-
if (ret < 0) {
326-
DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", ret);
327-
goto err_regulators_disable;
328-
}
329-
330-
msleep(50);
331-
332-
/*
333-
* The Manufacturer Command Set (MCS) is a device dependent interface
334-
* intended for factory programming of the display module default
335-
* parameters. Once the display module is configured, the MCS shall be
336-
* disabled by the manufacturer. Once disabled, all MCS commands are
337-
* ignored by the display interface.
338-
*/
339-
340-
ret = regmap_update_bits(map, PAGE2_MCS_EN, MCS_EN, 0);
341-
if (ret < 0) {
342-
DRM_ERROR("failed write PAGE2_MCS_EN: %d\n", ret);
343-
goto err_regulators_disable;
344-
}
345-
346-
/* Switch access edp panel's edid through i2c */
347-
ret = regmap_write(map, PAGE2_I2C_BYPASS, I2C_BYPASS_EN);
348-
if (ret < 0) {
349-
DRM_ERROR("failed write PAGE2_I2C_BYPASS: %d\n", ret);
350-
goto err_regulators_disable;
351-
}
352-
353-
ps_bridge->powered = true;
354-
355-
return;
356-
357-
err_regulators_disable:
358-
regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
359-
ps_bridge->supplies);
358+
return 0;
360359
}
361360

362-
static void ps8640_bridge_poweroff(struct ps8640 *ps_bridge)
361+
static int __maybe_unused ps8640_suspend(struct device *dev)
363362
{
363+
struct ps8640 *ps_bridge = dev_get_drvdata(dev);
364364
int ret;
365365

366-
if (!ps_bridge->powered)
367-
return;
368-
369366
gpiod_set_value(ps_bridge->gpio_reset, 1);
370367
gpiod_set_value(ps_bridge->gpio_powerdown, 1);
371368
ret = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
372369
ps_bridge->supplies);
373370
if (ret < 0)
374-
DRM_ERROR("cannot disable regulators %d\n", ret);
371+
dev_err(dev, "cannot disable regulators %d\n", ret);
375372

376-
ps_bridge->powered = false;
373+
return ret;
377374
}
378375

376+
static const struct dev_pm_ops ps8640_pm_ops = {
377+
SET_RUNTIME_PM_OPS(ps8640_suspend, ps8640_resume, NULL)
378+
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
379+
pm_runtime_force_resume)
380+
};
381+
379382
static void ps8640_pre_enable(struct drm_bridge *bridge)
380383
{
381384
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
385+
struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL];
386+
struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev;
382387
int ret;
383388

384-
ps8640_bridge_poweron(ps_bridge);
389+
pm_runtime_get_sync(dev);
390+
ps8640_ensure_hpd(ps_bridge);
385391

386-
ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
392+
/*
393+
* The Manufacturer Command Set (MCS) is a device dependent interface
394+
* intended for factory programming of the display module default
395+
* parameters. Once the display module is configured, the MCS shall be
396+
* disabled by the manufacturer. Once disabled, all MCS commands are
397+
* ignored by the display interface.
398+
*/
399+
400+
ret = regmap_update_bits(map, PAGE2_MCS_EN, MCS_EN, 0);
401+
if (ret < 0)
402+
dev_warn(dev, "failed write PAGE2_MCS_EN: %d\n", ret);
403+
404+
/* Switch access edp panel's edid through i2c */
405+
ret = regmap_write(map, PAGE2_I2C_BYPASS, I2C_BYPASS_EN);
387406
if (ret < 0)
388-
ps8640_bridge_poweroff(ps_bridge);
407+
dev_warn(dev, "failed write PAGE2_MCS_EN: %d\n", ret);
408+
409+
ps8640_bridge_vdo_control(ps_bridge, ENABLE);
410+
411+
ps_bridge->pre_enabled = true;
389412
}
390413

391414
static void ps8640_post_disable(struct drm_bridge *bridge)
392415
{
393416
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
394417

418+
ps_bridge->pre_enabled = false;
419+
395420
ps8640_bridge_vdo_control(ps_bridge, DISABLE);
396-
ps8640_bridge_poweroff(ps_bridge);
421+
pm_runtime_put_sync_suspend(&ps_bridge->page[PAGE0_DP_CNTL]->dev);
397422
}
398423

399424
static int ps8640_bridge_attach(struct drm_bridge *bridge,
@@ -426,7 +451,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
426451
struct drm_connector *connector)
427452
{
428453
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
429-
bool poweroff = !ps_bridge->powered;
454+
bool poweroff = !ps_bridge->pre_enabled;
430455
struct edid *edid;
431456

432457
/*
@@ -456,6 +481,12 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
456481
return edid;
457482
}
458483

484+
static void ps8640_runtime_disable(void *data)
485+
{
486+
pm_runtime_dont_use_autosuspend(data);
487+
pm_runtime_disable(data);
488+
}
489+
459490
static const struct drm_bridge_funcs ps8640_bridge_funcs = {
460491
.attach = ps8640_bridge_attach,
461492
.detach = ps8640_bridge_detach,
@@ -586,6 +617,22 @@ static int ps8640_probe(struct i2c_client *client)
586617
ps_bridge->aux.transfer = ps8640_aux_transfer;
587618
drm_dp_aux_init(&ps_bridge->aux);
588619

620+
pm_runtime_enable(dev);
621+
/*
622+
* Powering on ps8640 takes ~300ms. To avoid wasting time on power
623+
* cycling ps8640 too often, set autosuspend_delay to 500ms to ensure
624+
* the bridge wouldn't suspend in between each _aux_transfer_msg() call
625+
* during EDID read (~20ms in my experiment) and in between the last
626+
* _aux_transfer_msg() call during EDID read and the _pre_enable() call
627+
* (~100ms in my experiment).
628+
*/
629+
pm_runtime_set_autosuspend_delay(dev, 500);
630+
pm_runtime_use_autosuspend(dev);
631+
pm_suspend_ignore_children(dev, true);
632+
ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev);
633+
if (ret)
634+
return ret;
635+
589636
drm_bridge_add(&ps_bridge->bridge);
590637

591638
ret = ps8640_bridge_host_attach(dev, ps_bridge);
@@ -620,6 +667,7 @@ static struct i2c_driver ps8640_driver = {
620667
.driver = {
621668
.name = "ps8640",
622669
.of_match_table = ps8640_match,
670+
.pm = &ps8640_pm_ops,
623671
},
624672
};
625673
module_i2c_driver(ps8640_driver);

0 commit comments

Comments
 (0)