Commit e8b6fe69 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm/next/adv7511' of git://linuxtv.org/pinchartl/fbdev into drm-next

adv7511 fixes.

* 'drm/next/adv7511' of git://linuxtv.org/pinchartl/fbdev:
  drm: adv7511: Refactor power management
  drm: adv7511: Fix nested sleep when reading EDID
  drm: adv7511: Fix DDC error interrupt handling
parents b3ede177 c6169e49
...@@ -27,12 +27,13 @@ struct adv7511 { ...@@ -27,12 +27,13 @@ struct adv7511 {
struct regmap *regmap; struct regmap *regmap;
struct regmap *packet_memory_regmap; struct regmap *packet_memory_regmap;
enum drm_connector_status status; enum drm_connector_status status;
int dpms_mode; bool powered;
unsigned int f_tmds; unsigned int f_tmds;
unsigned int current_edid_segment; unsigned int current_edid_segment;
uint8_t edid_buf[256]; uint8_t edid_buf[256];
bool edid_read;
wait_queue_head_t wq; wait_queue_head_t wq;
struct drm_encoder *encoder; struct drm_encoder *encoder;
...@@ -357,6 +358,48 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, ...@@ -357,6 +358,48 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
} }
static void adv7511_power_on(struct adv7511 *adv7511)
{
adv7511->current_edid_segment = -1;
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
ADV7511_INT1_DDC_ERROR);
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, 0);
/*
* Per spec it is allowed to pulse the HDP signal to indicate that the
* EDID information has changed. Some monitors do this when they wakeup
* from standby or are enabled. When the HDP goes low the adv7511 is
* reset and the outputs are disabled which might cause the monitor to
* go to standby again. To avoid this we ignore the HDP pin for the
* first few seconds after enabling the output.
*/
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
ADV7511_REG_POWER2_HDP_SRC_MASK,
ADV7511_REG_POWER2_HDP_SRC_NONE);
/*
* Most of the registers are reset during power down or when HPD is low.
*/
regcache_sync(adv7511->regmap);
adv7511->powered = true;
}
static void adv7511_power_off(struct adv7511 *adv7511)
{
/* TODO: setup additional power down modes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN);
regcache_mark_dirty(adv7511->regmap);
adv7511->powered = false;
}
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Interrupt and hotplug detection * Interrupt and hotplug detection
*/ */
...@@ -379,69 +422,71 @@ static bool adv7511_hpd(struct adv7511 *adv7511) ...@@ -379,69 +422,71 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
return false; return false;
} }
static irqreturn_t adv7511_irq_handler(int irq, void *devid) static int adv7511_irq_process(struct adv7511 *adv7511)
{
struct adv7511 *adv7511 = devid;
if (adv7511_hpd(adv7511))
drm_helper_hpd_irq_event(adv7511->encoder->dev);
wake_up_all(&adv7511->wq);
return IRQ_HANDLED;
}
static unsigned int adv7511_is_interrupt_pending(struct adv7511 *adv7511,
unsigned int irq)
{ {
unsigned int irq0, irq1; unsigned int irq0, irq1;
unsigned int pending;
int ret; int ret;
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0); ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(0), &irq0);
if (ret < 0) if (ret < 0)
return 0; return ret;
ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1); ret = regmap_read(adv7511->regmap, ADV7511_REG_INT(1), &irq1);
if (ret < 0) if (ret < 0)
return 0; return ret;
pending = (irq1 << 8) | irq0; regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
return pending & irq; if (irq0 & ADV7511_INT0_HDP)
drm_helper_hpd_irq_event(adv7511->encoder->dev);
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true;
if (adv7511->i2c_main->irq)
wake_up_all(&adv7511->wq);
}
return 0;
} }
static int adv7511_wait_for_interrupt(struct adv7511 *adv7511, int irq, static irqreturn_t adv7511_irq_handler(int irq, void *devid)
int timeout) {
struct adv7511 *adv7511 = devid;
int ret;
ret = adv7511_irq_process(adv7511);
return ret < 0 ? IRQ_NONE : IRQ_HANDLED;
}
/* -----------------------------------------------------------------------------
* EDID retrieval
*/
static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
{ {
unsigned int pending;
int ret; int ret;
if (adv7511->i2c_main->irq) { if (adv7511->i2c_main->irq) {
ret = wait_event_interruptible_timeout(adv7511->wq, ret = wait_event_interruptible_timeout(adv7511->wq,
adv7511_is_interrupt_pending(adv7511, irq), adv7511->edid_read, msecs_to_jiffies(timeout));
msecs_to_jiffies(timeout));
if (ret <= 0)
return 0;
pending = adv7511_is_interrupt_pending(adv7511, irq);
} else { } else {
if (timeout < 25) for (; timeout > 0; timeout -= 25) {
timeout = 25; ret = adv7511_irq_process(adv7511);
do { if (ret < 0)
pending = adv7511_is_interrupt_pending(adv7511, irq);
if (pending)
break; break;
if (adv7511->edid_read)
break;
msleep(25); msleep(25);
timeout -= 25; }
} while (timeout >= 25);
} }
return pending; return adv7511->edid_read ? 0 : -EIO;
} }
/* -----------------------------------------------------------------------------
* EDID retrieval
*/
static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
size_t len) size_t len)
{ {
...@@ -463,19 +508,14 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, ...@@ -463,19 +508,14 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
return ret; return ret;
if (status != 2) { if (status != 2) {
adv7511->edid_read = false;
regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT, regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
block); block);
ret = adv7511_wait_for_interrupt(adv7511, ret = adv7511_wait_for_edid(adv7511, 200);
ADV7511_INT0_EDID_READY | if (ret < 0)
ADV7511_INT1_DDC_ERROR, 200); return ret;
if (!(ret & ADV7511_INT0_EDID_READY))
return -EIO;
} }
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
/* Break this apart, hopefully more I2C controllers will /* Break this apart, hopefully more I2C controllers will
* support 64 byte transfers than 256 byte transfers * support 64 byte transfers than 256 byte transfers
*/ */
...@@ -526,9 +566,11 @@ static int adv7511_get_modes(struct drm_encoder *encoder, ...@@ -526,9 +566,11 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
unsigned int count; unsigned int count;
/* Reading the EDID only works if the device is powered */ /* Reading the EDID only works if the device is powered */
if (adv7511->dpms_mode != DRM_MODE_DPMS_ON) { if (!adv7511->powered) {
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR); ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT(1),
ADV7511_INT1_DDC_ERROR);
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, 0); ADV7511_POWER_POWER_DOWN, 0);
adv7511->current_edid_segment = -1; adv7511->current_edid_segment = -1;
...@@ -536,7 +578,7 @@ static int adv7511_get_modes(struct drm_encoder *encoder, ...@@ -536,7 +578,7 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
if (adv7511->dpms_mode != DRM_MODE_DPMS_ON) if (!adv7511->powered)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN); ADV7511_POWER_POWER_DOWN);
...@@ -558,41 +600,10 @@ static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode) ...@@ -558,41 +600,10 @@ static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
{ {
struct adv7511 *adv7511 = encoder_to_adv7511(encoder); struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
switch (mode) { if (mode == DRM_MODE_DPMS_ON)
case DRM_MODE_DPMS_ON: adv7511_power_on(adv7511);
adv7511->current_edid_segment = -1; else
adv7511_power_off(adv7511);
regmap_write(adv7511->regmap, ADV7511_REG_INT(0),
ADV7511_INT0_EDID_READY | ADV7511_INT1_DDC_ERROR);
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, 0);
/*
* Per spec it is allowed to pulse the HDP signal to indicate
* that the EDID information has changed. Some monitors do this
* when they wakeup from standby or are enabled. When the HDP
* goes low the adv7511 is reset and the outputs are disabled
* which might cause the monitor to go to standby again. To
* avoid this we ignore the HDP pin for the first few seconds
* after enabling the output.
*/
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
ADV7511_REG_POWER2_HDP_SRC_MASK,
ADV7511_REG_POWER2_HDP_SRC_NONE);
/* Most of the registers are reset during power down or
* when HPD is low
*/
regcache_sync(adv7511->regmap);
break;
default:
/* TODO: setup additional power down modes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN);
regcache_mark_dirty(adv7511->regmap);
break;
}
adv7511->dpms_mode = mode;
} }
static enum drm_connector_status static enum drm_connector_status
...@@ -620,10 +631,9 @@ adv7511_encoder_detect(struct drm_encoder *encoder, ...@@ -620,10 +631,9 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
* there is a pending HPD interrupt and the cable is connected there was * there is a pending HPD interrupt and the cable is connected there was
* at least one transition from disconnected to connected and the chip * at least one transition from disconnected to connected and the chip
* has to be reinitialized. */ * has to be reinitialized. */
if (status == connector_status_connected && hpd && if (status == connector_status_connected && hpd && adv7511->powered) {
adv7511->dpms_mode == DRM_MODE_DPMS_ON) {
regcache_mark_dirty(adv7511->regmap); regcache_mark_dirty(adv7511->regmap);
adv7511_encoder_dpms(encoder, adv7511->dpms_mode); adv7511_power_on(adv7511);
adv7511_get_modes(encoder, connector); adv7511_get_modes(encoder, connector);
if (adv7511->status == connector_status_connected) if (adv7511->status == connector_status_connected)
status = connector_status_disconnected; status = connector_status_disconnected;
...@@ -858,7 +868,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ...@@ -858,7 +868,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
if (!adv7511) if (!adv7511)
return -ENOMEM; return -ENOMEM;
adv7511->dpms_mode = DRM_MODE_DPMS_OFF; adv7511->powered = false;
adv7511->status = connector_status_disconnected; adv7511->status = connector_status_disconnected;
ret = adv7511_parse_dt(dev->of_node, &link_config); ret = adv7511_parse_dt(dev->of_node, &link_config);
...@@ -918,10 +928,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ...@@ -918,10 +928,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
ADV7511_CEC_CTRL_POWER_DOWN); ADV7511_CEC_CTRL_POWER_DOWN);
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, adv7511_power_off(adv7511);
ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN);
adv7511->current_edid_segment = -1;
i2c_set_clientdata(i2c, adv7511); i2c_set_clientdata(i2c, adv7511);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment