mirror of
https://gitlab.freedesktop.org/mesa/drm.git
synced 2025-12-25 21:10:11 +01:00
NV50: Do detect with hpd and load detect if possible.
- Appropriate error messages when an unknown situation is encountered are included. - Fallback to i2c will occur when needed.
This commit is contained in:
parent
685bca02fe
commit
d00644c27d
6 changed files with 144 additions and 39 deletions
|
|
@ -76,7 +76,45 @@ static struct nv50_output *nv50_connector_to_output(struct nv50_connector *conne
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static bool nv50_connector_detect(struct nv50_connector *connector)
|
||||
static int nv50_connector_hpd_detect(struct nv50_connector *connector)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
||||
struct nv50_output *output = NULL;
|
||||
bool present = 0;
|
||||
uint32_t reg = 0;
|
||||
|
||||
/* Assume connected for the moment. */
|
||||
if (connector->type == CONNECTOR_LVDS) {
|
||||
NV50_DEBUG("LVDS is defaulting to connected for the moment.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No i2c port, no idea what to do for hotplug. */
|
||||
if (connector->i2c_chan->index == 15) {
|
||||
DRM_ERROR("You have a non-LVDS SOR with no i2c port, please report\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (connector->i2c_chan->index > 3) {
|
||||
DRM_ERROR("You have an unusual configuration, index is %d\n", connector->i2c_chan->index);
|
||||
DRM_ERROR("Please report.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check hotplug pins. */
|
||||
reg = NV_READ(NV50_PCONNECTOR_HOTPLUG_STATE);
|
||||
if (reg & (NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 << (4 * connector->i2c_chan->index)))
|
||||
present = 1;
|
||||
|
||||
if (present)
|
||||
NV50_DEBUG("Hotplug detect returned positive for bus %d\n", connector->bus);
|
||||
else
|
||||
NV50_DEBUG("Hotplug detect returned negative for bus %d\n", connector->bus);
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
static int nv50_connector_i2c_detect(struct nv50_connector *connector)
|
||||
{
|
||||
/* kindly borrrowed from the intel driver, hope it works. */
|
||||
uint8_t out_buf[] = { 0x0, 0x0};
|
||||
|
|
@ -97,13 +135,11 @@ static bool nv50_connector_detect(struct nv50_connector *connector)
|
|||
}
|
||||
};
|
||||
|
||||
NV50_DEBUG("\n");
|
||||
|
||||
if (!connector->i2c_chan)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2);
|
||||
DRM_INFO("I2C detect returned %d\n", ret);
|
||||
NV50_DEBUG("I2C detect returned %d\n", ret);
|
||||
|
||||
if (ret == 2)
|
||||
return true;
|
||||
|
|
@ -195,7 +231,8 @@ int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int ty
|
|||
connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index);
|
||||
|
||||
/* set function pointers */
|
||||
connector->detect = nv50_connector_detect;
|
||||
connector->hpd_detect = nv50_connector_hpd_detect;
|
||||
connector->i2c_detect = nv50_connector_i2c_detect;
|
||||
connector->destroy = nv50_connector_destroy;
|
||||
connector->to_output = nv50_connector_to_output;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ struct nv50_connector {
|
|||
|
||||
bool use_dithering;
|
||||
|
||||
bool (*detect) (struct nv50_connector *connector);
|
||||
int (*hpd_detect) (struct nv50_connector *connector);
|
||||
int (*i2c_detect) (struct nv50_connector *connector);
|
||||
int (*destroy) (struct nv50_connector *connector);
|
||||
struct nv50_output *(*to_output) (struct nv50_connector *connector, bool digital);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,6 +133,39 @@ static int nv50_dac_set_power_mode(struct nv50_output *output, int mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nv50_dac_detect(struct nv50_output *output)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = output->dev->dev_private;
|
||||
int or = nv50_output_or_offset(output);
|
||||
bool present = 0;
|
||||
uint32_t dpms_state, load_pattern, load_state;
|
||||
|
||||
NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(or), 0x00000001);
|
||||
dpms_state = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or));
|
||||
|
||||
NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), 0x00150000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING);
|
||||
while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING);
|
||||
|
||||
load_pattern = 340; /* TODO: use a bios table for this */
|
||||
|
||||
NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE | load_pattern);
|
||||
udelay(10000); /* give it some time to process */
|
||||
load_state = NV_READ(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or));
|
||||
|
||||
NV_WRITE(NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(or), 0);
|
||||
NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), dpms_state);
|
||||
|
||||
if ((load_state & NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT) == NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT)
|
||||
present = 1;
|
||||
|
||||
if (present)
|
||||
NV50_DEBUG("Load was detected on output with or %d\n", or);
|
||||
else
|
||||
NV50_DEBUG("Load was not detected on output with or %d\n", or);
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
static int nv50_dac_destroy(struct nv50_output *output)
|
||||
{
|
||||
struct drm_device *dev = output->dev;
|
||||
|
|
@ -210,7 +243,7 @@ int nv50_dac_create(struct drm_device *dev, int dcb_entry)
|
|||
output->execute_mode = nv50_dac_execute_mode;
|
||||
output->set_clock_mode = nv50_dac_set_clock_mode;
|
||||
output->set_power_mode = nv50_dac_set_power_mode;
|
||||
output->detect = NULL; /* TODO */
|
||||
output->detect = nv50_dac_detect;
|
||||
output->destroy = nv50_dac_destroy;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
|
|||
/* This is to ensure it knows the connector subtype. */
|
||||
drm_connector->funcs->fill_modes(drm_connector, 0, 0);
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
|
|
@ -458,7 +458,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
|
|||
goto out;
|
||||
}
|
||||
|
||||
output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
if (!output) {
|
||||
DRM_ERROR("No output\n");
|
||||
goto out;
|
||||
|
|
@ -835,7 +835,9 @@ static int nv50_kms_encoders_init(struct drm_device *dev)
|
|||
* Connector functions
|
||||
*/
|
||||
|
||||
bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
|
||||
|
||||
/* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */
|
||||
bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector)
|
||||
{
|
||||
struct drm_device *dev = drm_connector->dev;
|
||||
|
||||
|
|
@ -892,6 +894,33 @@ bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force)
|
||||
{
|
||||
struct drm_device *dev = drm_connector->dev;
|
||||
|
||||
if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
|
||||
uint64_t cur_value, new_value;
|
||||
|
||||
int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value);
|
||||
if (rval) {
|
||||
DRM_ERROR("Unable to find subconnector property\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only set when unknown or when forced to do so. */
|
||||
if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force)
|
||||
return;
|
||||
|
||||
if (digital == 1)
|
||||
new_value = DRM_MODE_SUBCONNECTOR_DVID;
|
||||
else if (digital == 0)
|
||||
new_value = DRM_MODE_SUBCONNECTOR_DVIA;
|
||||
else
|
||||
new_value = DRM_MODE_SUBCONNECTOR_Unknown;
|
||||
drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
void nv50_kms_connector_detect_all(struct drm_device *dev)
|
||||
{
|
||||
struct drm_connector *drm_connector = NULL;
|
||||
|
|
@ -903,16 +932,32 @@ void nv50_kms_connector_detect_all(struct drm_device *dev)
|
|||
|
||||
static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector)
|
||||
{
|
||||
struct nv50_connector *connector = to_nv50_connector(drm_connector);
|
||||
struct drm_device *dev = drm_connector->dev;
|
||||
bool connected;
|
||||
int old_status;
|
||||
struct nv50_connector *connector = to_nv50_connector(drm_connector);
|
||||
struct nv50_output *output = NULL;
|
||||
int hpd_detect = 0, load_detect = 0, i2c_detect = 0;
|
||||
int old_status = drm_connector->status;
|
||||
|
||||
connected = connector->detect(connector);
|
||||
/* hotplug detect */
|
||||
hpd_detect = connector->hpd_detect(connector);
|
||||
|
||||
old_status = drm_connector->status;
|
||||
/* load detect */
|
||||
output = connector->to_output(connector, FALSE); /* analog */
|
||||
if (output && output->detect)
|
||||
load_detect = output->detect(output);
|
||||
|
||||
if (connected)
|
||||
if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */
|
||||
i2c_detect = connector->i2c_detect(connector);
|
||||
|
||||
if (load_detect == 1) {
|
||||
nv50_kms_connector_set_digital(drm_connector, 0, TRUE); /* analog, forced */
|
||||
} else if (hpd_detect == 1 && load_detect == 0) {
|
||||
nv50_kms_connector_set_digital(drm_connector, 1, TRUE); /* digital, forced */
|
||||
} else {
|
||||
nv50_kms_connector_set_digital(drm_connector, -1, TRUE); /* unknown, forced */
|
||||
}
|
||||
|
||||
if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1)
|
||||
drm_connector->status = connector_status_connected;
|
||||
else
|
||||
drm_connector->status = connector_status_disconnected;
|
||||
|
|
@ -956,7 +1001,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
struct nv50_connector *connector = to_nv50_connector(drm_connector);
|
||||
struct drm_device *dev = drm_connector->dev;
|
||||
int rval = 0;
|
||||
bool connected;
|
||||
bool connected = false;
|
||||
struct drm_display_mode *mode, *t;
|
||||
struct edid *edid = NULL;
|
||||
|
||||
|
|
@ -965,16 +1010,13 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
list_for_each_entry_safe(mode, t, &drm_connector->modes, head)
|
||||
mode->status = MODE_UNVERIFIED;
|
||||
|
||||
connected = connector->detect(connector);
|
||||
if (nv50_kms_connector_detect(drm_connector) == connector_status_connected)
|
||||
connected = true;
|
||||
|
||||
if (connected)
|
||||
drm_connector->status = connector_status_connected;
|
||||
NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector));
|
||||
else
|
||||
drm_connector->status = connector_status_disconnected;
|
||||
|
||||
if (!connected) {
|
||||
NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector));
|
||||
}
|
||||
|
||||
/* Not all connnectors have an i2c channel. */
|
||||
if (connected && connector->i2c_chan)
|
||||
|
|
@ -986,16 +1028,8 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
if (edid) {
|
||||
rval = drm_add_edid_modes(drm_connector, edid);
|
||||
|
||||
/* 2 encoders per connector */
|
||||
/* eventually do this based on load detect and hot plug detect */
|
||||
if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
|
||||
uint64_t subtype = 0;
|
||||
if (edid->digital)
|
||||
subtype = DRM_MODE_SUBCONNECTOR_DVID;
|
||||
else
|
||||
subtype = DRM_MODE_SUBCONNECTOR_DVIA;
|
||||
drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, subtype);
|
||||
}
|
||||
/* Only update when relevant and when detect couldn't determine type. */
|
||||
nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, FALSE);
|
||||
|
||||
kfree(edid);
|
||||
}
|
||||
|
|
@ -1009,7 +1043,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
|
||||
if (mode->status == MODE_OK) {
|
||||
struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
|
||||
struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
|
||||
struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
|
||||
mode->status = output->validate_mode(output, hw_mode);
|
||||
/* find native mode, TODO: also check if we actually found one */
|
||||
|
|
@ -1025,7 +1059,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
|
||||
if (mode->status == MODE_OK) {
|
||||
struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
|
||||
struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
|
||||
struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
|
||||
mode->status = output->validate_mode(output, hw_mode);
|
||||
kfree(hw_mode);
|
||||
|
|
@ -1057,7 +1091,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
|
|||
|
||||
/* also add it as native mode */
|
||||
hw_mode = nv50_kms_to_hw_mode(mode);
|
||||
output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
|
||||
output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector));
|
||||
|
||||
if (hw_mode)
|
||||
*output->native_mode = *hw_mode;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ struct nv50_kms_priv {
|
|||
|
||||
struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev);
|
||||
void nv50_kms_connector_detect_all(struct drm_device *dev);
|
||||
bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector);
|
||||
bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector);
|
||||
|
||||
int nv50_kms_init(struct drm_device *dev);
|
||||
int nv50_kms_destroy(struct drm_device *dev);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct nv50_output {
|
|||
int (*set_clock_mode) (struct nv50_output *output);
|
||||
/* this is not a normal modeset call, it is a direct register write, so it's executed immediately */
|
||||
int (*set_power_mode) (struct nv50_output *output, int mode);
|
||||
bool (*detect) (struct nv50_output *output);
|
||||
int (*detect) (struct nv50_output *output);
|
||||
int (*destroy) (struct nv50_output *output);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue