Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 01/10] drm/nouveau: Fix a lock up at NVSetOwner with nv11.
It seems it was only locking up in the context of
nouveau_hw_save_vga_fonts, when it actually did something (because
the console wasn't already in graphics mode).
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_hw.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c
b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 6f55f55..d270d6f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -87,8 +87,17 @@ NVSetOwner(struct drm_device *dev, int owner)
if (owner == 1)
owner *= 3;
+ if (dev_priv->chipset == 0x11) {
+ /* This might seem stupid, but the blob does it and
+ * omitting it often locks the system up.
+ */
+ NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
+ NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX);
+ }
+
/* CR44 is always changed on CRTC0 */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
+
if (dev_priv->chipset == 0x11) { /* set me harder */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 02/10] drm/nouveau: Fix fbcon with multiple outputs connected.
vgacon attempts to restore the CRTC base on deinit so it messes up the
second head scanout address. Call crtc->helper_funcs->mode_set_base
directly from nouveau_fbcon_pan_display to ensure it's written out
even if the coordinates haven't changed. Modify nouveau_fbcon_set_par
so that it always calls crtc->funcs->set_config.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_fbcon.c | 18 +++++++++---------
1 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 0e6ebaa..645b087 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -282,7 +282,7 @@ static int nouveau_fbcon_set_par(struct fb_info *info)
int ret;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ struct drm_mode_set *modeset = &nouveau_crtc(crtc)->mode_set;
for (i = 0; i < par->crtc_count; i++)
if (crtc->base.id == par->crtc_ids[i])
@@ -291,9 +291,9 @@ static int nouveau_fbcon_set_par(struct fb_info *info)
if (i == par->crtc_count)
continue;
- if (crtc->fb == nv_crtc->mode_set.fb) {
+ if (modeset->num_connectors) {
mutex_lock(&dev->mode_config.mutex);
- ret = crtc->funcs->set_config(&nv_crtc->mode_set);
+ ret = crtc->funcs->set_config(modeset);
mutex_unlock(&dev->mode_config.mutex);
if (ret)
return ret;
@@ -310,7 +310,7 @@ static int nouveau_fbcon_pan_display(struct
fb_var_screeninfo *var,
struct drm_device *dev = par->dev;
struct drm_mode_set *modeset;
struct drm_crtc *crtc;
- struct nouveau_crtc *nv_crtc;
+ struct drm_crtc_helper_funcs *helper_funcs;
int ret = 0;
int i;
@@ -322,16 +322,16 @@ static int nouveau_fbcon_pan_display(struct
fb_var_screeninfo *var,
if (i == par->crtc_count)
continue;
- nv_crtc = nouveau_crtc(crtc);
- modeset = &nv_crtc->mode_set;
+ helper_funcs = crtc->helper_private;
+ modeset = &nouveau_crtc(crtc)->mode_set;
modeset->x = var->xoffset;
modeset->y = var->yoffset;
if (modeset->num_connectors) {
- mutex_lock(&dev->mode_config.mutex);
- ret = crtc->funcs->set_config(modeset);
- mutex_unlock(&dev->mode_config.mutex);
+ ret = helper_funcs->mode_set_base(crtc,
+ modeset->x, modeset->y, modeset->fb);
+
if (!ret) {
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 03/10] drm/nouveau: Use drm_encoder_slave instead of drm_encoder.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_connector.c | 8 ++++----
drivers/gpu/drm/nouveau/nouveau_encoder.h | 13 +++++++++++--
drivers/gpu/drm/nouveau/nv04_output.c | 8 ++++----
drivers/gpu/drm/nouveau/nv50_crtc.c | 2 +-
drivers/gpu/drm/nouveau/nv50_dac.c | 18 +++++++++---------
drivers/gpu/drm/nouveau/nv50_sor.c | 18 +++++++++---------
6 files changed, 38 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c
b/drivers/gpu/drm/nouveau/nouveau_connector.c
index c89bef7..54d5e58 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -221,14 +221,14 @@ nouveau_connector_detect(struct drm_connector *connector)
nv_encoder = nouveau_connector_encoder_get(connector, OUTPUT_ANALOG);
if (!nouveau_connector_ddc_detect(connector)) {
- if (!nv_encoder || !nv_encoder->base.helper_private)
+ if (!nv_encoder || !to_drm_encoder(nv_encoder)->helper_private)
return connector_status_disconnected;
- helper = nv_encoder->base.helper_private;
+ helper = to_drm_encoder(nv_encoder)->helper_private;
if (!helper || !helper->detect)
return connector_status_disconnected;
- if (helper->detect(&nv_encoder->base, connector) =+ if
(helper->detect(to_drm_encoder(nv_encoder), connector) =
connector_status_connected) {
nouveau_connector_set_encoder(connector, nv_encoder);
return connector_status_connected;
@@ -447,7 +447,7 @@ nouveau_connector_best_encoder(struct drm_connector
*connector)
struct nouveau_connector *nv_connector = nouveau_connector(connector);
if (nv_connector->detected_encoder)
- return &nv_connector->detected_encoder->base;
+ return to_drm_encoder(nv_connector->detected_encoder);
return NULL;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h
b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index 3344e6c..8a7369c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -27,16 +27,18 @@
#ifndef __NOUVEAU_OUTPUT_H__
#define __NOUVEAU_OUTPUT_H__
+#include "drm_encoder_slave.h"
#include "nouveau_drv.h"
#define NV_DPMS_CLEARED 0x80
struct nouveau_encoder {
- struct drm_encoder base;
+ struct drm_encoder_slave base;
struct dcb_entry *dcb;
int or;
+ struct drm_display_mode mode;
int last_dpms;
struct nv04_output_reg restore;
@@ -44,7 +46,14 @@ struct nouveau_encoder {
static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
{
- return container_of(enc, struct nouveau_encoder, base);
+ struct drm_encoder_slave *slave = to_encoder_slave(enc);
+
+ return container_of(slave, struct nouveau_encoder, base);
+}
+
+static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
+{
+ return &enc->base.base;
}
int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
diff --git a/drivers/gpu/drm/nouveau/nv04_output.c
b/drivers/gpu/drm/nouveau/nv04_output.c
index 9bfeba4..ba57dcf 100644
--- a/drivers/gpu/drm/nouveau/nv04_output.c
+++ b/drivers/gpu/drm/nouveau/nv04_output.c
@@ -786,11 +786,11 @@ nv04_encoder_create(struct drm_device *dev, struct
dcb_entry *entry)
} else
helper = &nv04_encoder_helper_funcs;
- drm_encoder_init(dev, &nv_encoder->base, &nv04_encoder_funcs,
type);
- drm_encoder_helper_add(&nv_encoder->base, helper);
+ drm_encoder_init(dev, to_drm_encoder(nv_encoder), &nv04_encoder_funcs,
type);
+ drm_encoder_helper_add(to_drm_encoder(nv_encoder), helper);
- nv_encoder->base.possible_crtcs = entry->heads;
- nv_encoder->base.possible_clones = 0;
+ to_drm_encoder(nv_encoder)->possible_crtcs = entry->heads;
+ to_drm_encoder(nv_encoder)->possible_clones = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c
b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 99f24a3..d319a73 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -217,7 +217,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *crtc)
return NULL;
list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
- if (drm_connector->encoder == &encoder->base)
+ if (drm_connector->encoder == to_drm_encoder(encoder))
return nouveau_connector(drm_connector);
}
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c
b/drivers/gpu/drm/nouveau/nv50_dac.c
index f07b92a..67b1457 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -36,7 +36,7 @@
static void
nv50_dac_disconnect(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
@@ -57,7 +57,7 @@ nv50_dac_detect(struct drm_encoder *drm_encoder,
struct drm_connector *drm_connector)
{
struct nouveau_encoder *encoder = nouveau_encoder(drm_encoder);
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
enum drm_connector_status status = connector_status_disconnected;
uint32_t dpms_state, load_pattern, load_state;
@@ -207,10 +207,10 @@ static void nv50_dac_mode_set(struct drm_encoder
*drm_encoder,
mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
/* Lacking a working tv-out, this is not a 100% sure. */
- if (encoder->base.encoder_type == DRM_MODE_ENCODER_DAC) {
+ if (to_drm_encoder(encoder)->encoder_type == DRM_MODE_ENCODER_DAC) {
mode_ctl |= 0x40;
} else
- if (encoder->base.encoder_type == DRM_MODE_ENCODER_TVDAC) {
+ if (to_drm_encoder(encoder)->encoder_type == DRM_MODE_ENCODER_TVDAC) {
mode_ctl |= 0x100;
}
@@ -250,7 +250,7 @@ static void nv50_dac_destroy(struct drm_encoder
*drm_encoder)
if (!drm_encoder)
return;
- drm_encoder_cleanup(&encoder->base);
+ drm_encoder_cleanup(to_drm_encoder(encoder));
kfree(encoder);
}
@@ -273,12 +273,12 @@ int nv50_dac_create(struct drm_device *dev, struct
dcb_entry *entry)
encoder->dcb = entry;
encoder->or = ffs(entry->or) - 1;
- drm_encoder_init(dev, &encoder->base, &nv50_dac_encoder_funcs,
+ drm_encoder_init(dev, to_drm_encoder(encoder), &nv50_dac_encoder_funcs,
DRM_MODE_ENCODER_DAC);
- drm_encoder_helper_add(&encoder->base, &nv50_dac_helper_funcs);
+ drm_encoder_helper_add(to_drm_encoder(encoder), &nv50_dac_helper_funcs);
- encoder->base.possible_crtcs = entry->heads;
- encoder->base.possible_clones = 0;
+ to_drm_encoder(encoder)->possible_crtcs = entry->heads;
+ to_drm_encoder(encoder)->possible_clones = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c
b/drivers/gpu/drm/nouveau/nv50_sor.c
index af66324..60fe995 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -37,7 +37,7 @@
static void
nv50_sor_disconnect(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
@@ -106,11 +106,11 @@ static void nv50_sor_restore(struct drm_encoder
*drm_encoder)
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_connector *drm_connector;
list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
- if (drm_connector->encoder == &encoder->base)
+ if (drm_connector->encoder == to_drm_encoder(encoder))
return nouveau_connector(drm_connector);
}
@@ -166,7 +166,7 @@ static void nv50_sor_mode_set(struct drm_encoder
*drm_encoder,
nv50_sor_dpms(drm_encoder, DRM_MODE_DPMS_ON);
dev_priv->in_modeset = ret;
- if (encoder->base.encoder_type != DRM_MODE_ENCODER_LVDS) {
+ if (to_drm_encoder(encoder)->encoder_type != DRM_MODE_ENCODER_LVDS) {
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_TMDS;
if (adjusted_mode->clock > 165000)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK;
@@ -212,7 +212,7 @@ static void nv50_sor_destroy(struct drm_encoder
*drm_encoder)
if (!drm_encoder)
return;
- drm_encoder_cleanup(&encoder->base);
+ drm_encoder_cleanup(to_drm_encoder(encoder));
kfree(encoder);
}
@@ -254,11 +254,11 @@ int nv50_sor_create(struct drm_device *dev, struct
dcb_entry *entry)
encoder->dcb = entry;
encoder->or = ffs(entry->or) - 1;
- drm_encoder_init(dev, &encoder->base, &nv50_sor_encoder_funcs,
type);
- drm_encoder_helper_add(&encoder->base, &nv50_sor_helper_funcs);
+ drm_encoder_init(dev, to_drm_encoder(encoder), &nv50_sor_encoder_funcs,
type);
+ drm_encoder_helper_add(to_drm_encoder(encoder), &nv50_sor_helper_funcs);
- encoder->base.possible_crtcs = entry->heads;
- encoder->base.possible_clones = 0;
+ to_drm_encoder(encoder)->possible_crtcs = entry->heads;
+ to_drm_encoder(encoder)->possible_clones = 0;
return 0;
}
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 04/10] drm/nouveau: Prepare the connector code for TV-out.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_connector.c | 202 +++++++++++++-------------
drivers/gpu/drm/nouveau/nv50_sor.c | 14 --
2 files changed, 101 insertions(+), 115 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c
b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 54d5e58..34870d7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -34,8 +34,14 @@
#include "nouveau_connector.h"
#include "nouveau_hw.h"
+static inline struct drm_encoder_slave_funcs *
+get_slave_funcs(struct nouveau_encoder *enc)
+{
+ return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
+}
+
static struct nouveau_encoder *
-nouveau_connector_encoder_get(struct drm_connector *connector, int type)
+find_encoder_by_type(struct drm_connector *connector, int type)
{
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder;
@@ -59,6 +65,21 @@ nouveau_connector_encoder_get(struct drm_connector
*connector, int type)
return NULL;
}
+struct nouveau_connector *
+nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
+{
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
+ struct drm_connector *drm_connector;
+
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
+ if (drm_connector->encoder == to_drm_encoder(encoder))
+ return nouveau_connector(drm_connector);
+ }
+
+ return NULL;
+}
+
+
static void
nouveau_connector_destroy(struct drm_connector *drm_connector)
{
@@ -173,95 +194,63 @@ nouveau_connector_set_encoder(struct drm_connector
*connector,
}
static enum drm_connector_status
-nouveau_connector_lvds_detect(struct drm_connector *connector)
-{
- struct nouveau_connector *nv_connector = nouveau_connector(connector);
- struct nouveau_encoder *nv_encoder;
- struct drm_device *dev = connector->dev;
- int flags;
-
- nv_encoder = nouveau_connector_encoder_get(connector, OUTPUT_LVDS);
- if (!nv_encoder) {
- NV_ERROR(dev, "LVDS but no encoder!\n");
- return connector_status_disconnected;
- }
-
- if (nv_connector->native_mode) {
- nouveau_connector_set_encoder(connector, nv_encoder);
- return connector_status_connected;
- }
-
- nouveau_connector_ddc_prepare(connector, &flags);
- nv_connector->edid - drm_get_edid(connector,
&nv_connector->i2c_chan->adapter);
- nouveau_connector_ddc_finish(connector, flags);
- drm_mode_connector_update_edid_property(connector, nv_connector->edid);
- if (!nv_connector->edid) {
- NV_ERROR(dev, "LVDS but no modes!\n");
- return connector_status_disconnected;
- }
-
- nouveau_connector_set_encoder(connector, nv_encoder);
- return connector_status_connected;
-}
-
-static enum drm_connector_status
nouveau_connector_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
- struct drm_encoder_helper_funcs *helper = NULL;
struct nouveau_encoder *nv_encoder = NULL;
int type, flags;
- nv_connector->detected_encoder = NULL;
+ if ((nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS))
+ && nv_connector->native_mode) {
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
- return nouveau_connector_lvds_detect(connector);
+ nouveau_connector_set_encoder(connector, nv_encoder);
+ return connector_status_connected;
+ }
- nv_encoder = nouveau_connector_encoder_get(connector, OUTPUT_ANALOG);
- if (!nouveau_connector_ddc_detect(connector)) {
- if (!nv_encoder || !to_drm_encoder(nv_encoder)->helper_private)
+ if (nouveau_connector_ddc_detect(connector)) {
+ nouveau_connector_ddc_prepare(connector, &flags);
+ nv_connector->edid + drm_get_edid(connector,
&nv_connector->i2c_chan->adapter);
+ nouveau_connector_ddc_finish(connector, flags);
+ drm_mode_connector_update_edid_property(connector, nv_connector->edid);
+ if (!nv_connector->edid) {
+ NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
+ drm_get_connector_name(connector));
return connector_status_disconnected;
+ }
- helper = to_drm_encoder(nv_encoder)->helper_private;
- if (!helper || !helper->detect)
- return connector_status_disconnected;
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+ type = OUTPUT_LVDS;
+ else if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
+ type = OUTPUT_TMDS;
+ else
+ type = OUTPUT_ANALOG;
- if (helper->detect(to_drm_encoder(nv_encoder), connector) =-
connector_status_connected) {
- nouveau_connector_set_encoder(connector, nv_encoder);
- return connector_status_connected;
+ nv_encoder = find_encoder_by_type(connector, type);
+ if (!nv_encoder) {
+ NV_ERROR(dev, "Detected %d encoder on %s, but no object!\n",
+ type, drm_get_connector_name(connector));
+ return connector_status_disconnected;
}
- return connector_status_disconnected;
+ nouveau_connector_set_encoder(connector, nv_encoder);
+ return connector_status_connected;
}
- nouveau_connector_ddc_prepare(connector, &flags);
- nv_connector->edid - drm_get_edid(connector,
&nv_connector->i2c_chan->adapter);
- nouveau_connector_ddc_finish(connector, flags);
- drm_mode_connector_update_edid_property(connector, nv_connector->edid);
- if (!nv_connector->edid) {
- NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
- drm_get_connector_name(connector));
- return connector_status_disconnected;
- }
+ if ((nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG)) ||
+ (nv_encoder = find_encoder_by_type(connector, OUTPUT_TV))) {
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
- if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
- type = OUTPUT_TMDS;
- else
- type = OUTPUT_ANALOG;
+ if (helper->detect(encoder, connector) == connector_status_connected) {
+ nouveau_connector_set_encoder(connector, nv_encoder);
+ return connector_status_connected;
+ }
- nv_encoder = nouveau_connector_encoder_get(connector, type);
- if (!nv_encoder) {
- NV_ERROR(dev, "Detected %d encoder on %s, but no object!\n",
- type, drm_get_connector_name(connector));
- return connector_status_disconnected;
}
- nouveau_connector_set_encoder(connector, nv_encoder);
- return connector_status_connected;
+ return connector_status_disconnected;
}
static int
@@ -269,6 +258,7 @@ nouveau_connector_set_property(struct drm_connector
*connector,
struct drm_property *property, uint64_t value)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct drm_device *dev = connector->dev;
int ret;
@@ -310,8 +300,8 @@ nouveau_connector_set_property(struct drm_connector
*connector,
&nv_crtc->base.mode,
nv_crtc->base.x,
nv_crtc->base.y, NULL);
- if (ret)
- return ret;
+ if (!ret)
+ return -EINVAL;
} else {
ret = nv_crtc->set_scale(nv_crtc, value, true);
if (ret)
@@ -340,6 +330,11 @@ nouveau_connector_set_property(struct drm_connector
*connector,
true);
}
+
+ if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
+ return get_slave_funcs(nv_encoder)->
+ set_property(to_drm_encoder(nv_encoder), connector, property, value);
+
return -EINVAL;
}
@@ -348,8 +343,9 @@ nouveau_connector_native_mode(struct nouveau_connector
*connector)
{
struct drm_device *dev = connector->base.dev;
struct drm_display_mode *mode;
+ uint8_t type = connector->detected_encoder->dcb->type;
- if (connector->detected_encoder->dcb->type == OUTPUT_ANALOG)
+ if (type != OUTPUT_LVDS && type != OUTPUT_TMDS)
return NULL;
list_for_each_entry(mode, &connector->base.probed_modes, head) {
@@ -363,8 +359,9 @@ nouveau_connector_native_mode(struct nouveau_connector
*connector)
static int
nouveau_connector_get_modes(struct drm_connector *connector)
{
- struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct drm_device *dev = connector->dev;
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
int ret = 0;
/* If we're not LVDS, destroy the previous native mode, the attached
@@ -394,6 +391,10 @@ nouveau_connector_get_modes(struct drm_connector
*connector)
ret = 1;
}
+ if (nv_encoder->dcb->type == OUTPUT_TV)
+ ret = get_slave_funcs(nv_encoder)->
+ get_modes(to_drm_encoder(nv_encoder), connector);
+
return ret;
}
@@ -404,9 +405,7 @@ nouveau_connector_mode_valid(struct drm_connector
*connector,
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
- unsigned min_clock, max_clock;
-
- min_clock = 25000;
+ unsigned min_clock = 25000, max_clock = min_clock;
switch (nv_encoder->dcb->type) {
case OUTPUT_LVDS:
@@ -425,11 +424,15 @@ nouveau_connector_mode_valid(struct drm_connector
*connector,
else
max_clock = 330000;
break;
- default:
+ case OUTPUT_ANALOG:
max_clock = nv_encoder->dcb->crtconf.maxfreq;
if (!max_clock)
max_clock = 350000;
break;
+ case OUTPUT_TV:
+ return get_slave_funcs(nv_encoder)->
+ mode_valid(to_drm_encoder(nv_encoder), mode);
+
}
if (mode->clock < min_clock)
@@ -523,7 +526,7 @@ nouveau_connector_create(struct drm_device *dev, int
i2c_index, int type)
return ret;
}
break;
- case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_TV:
NV_INFO(dev, "Detected a TV connector\n");
break;
default:
@@ -531,21 +534,6 @@ nouveau_connector_create(struct drm_device *dev, int
i2c_index, int type)
break;
}
- /* some reasonable defaults */
- switch (type) {
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_DVID:
- case DRM_MODE_CONNECTOR_LVDS:
- nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
- break;
- default:
- nv_connector->scaling_mode = DRM_MODE_SCALE_NON_GPU;
- break;
- }
-
- if (type != DRM_MODE_CONNECTOR_LVDS)
- nv_connector->use_dithering = false;
-
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
@@ -569,14 +557,23 @@ nouveau_connector_create(struct drm_device *dev, int
i2c_index, int type)
drm_connector_attach_property(connector,
dev->mode_config.dvi_i_select_subconnector_property, 0);
}
- /* If supported in the future, it will have to use the scalers
- * internally and not expose them.
- */
- if (type != DRM_MODE_CONNECTOR_SVIDEO) {
- drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property, nv_connector->scaling_mode);
- }
+ if (type != DRM_MODE_CONNECTOR_LVDS)
+ nv_connector->use_dithering = false;
+
+ if (type == DRM_MODE_CONNECTOR_DVID ||
+ type == DRM_MODE_CONNECTOR_DVII ||
+ type == DRM_MODE_CONNECTOR_LVDS) {
+ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
- drm_connector_attach_property(connector,
dev->mode_config.dithering_mode_property, nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
+ drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property,
+ nv_connector->scaling_mode);
+ drm_connector_attach_property(connector,
dev->mode_config.dithering_mode_property,
+ nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
+ : DRM_MODE_DITHERING_OFF);
+
+ } else {
+ nv_connector->scaling_mode = DRM_MODE_SCALE_NON_GPU;
+ }
/* attach encoders */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@@ -585,6 +582,9 @@ nouveau_connector_create(struct drm_device *dev, int
i2c_index, int type)
if (nv_encoder->dcb->i2c_index != i2c_index)
continue;
+ if (get_slave_funcs(nv_encoder))
+ get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
+
drm_mode_connector_attach_encoder(connector, encoder);
}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c
b/drivers/gpu/drm/nouveau/nv50_sor.c
index 60fe995..fb973fa 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -103,20 +103,6 @@ static void nv50_sor_restore(struct drm_encoder
*drm_encoder)
NV_ERROR(drm_encoder->dev, "!!\n");
}
-struct nouveau_connector *
-nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
-{
- struct drm_device *dev = to_drm_encoder(encoder)->dev;
- struct drm_connector *drm_connector;
-
- list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
- if (drm_connector->encoder == to_drm_encoder(encoder))
- return nouveau_connector(drm_connector);
- }
-
- return NULL;
-}
-
static bool nv50_sor_mode_fixup(struct drm_encoder *drm_encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 05/10] drm/nouveau: Restructure the nv04 modesetting code.
* Delay the hardware state synchronization to crtc->commit(). This way
the encoder-specific code can adjust the CRTC regs on mode_set()
before they're written out.
* Don't abuse adjusted_mode to mean the output side mode, this makes
impossible for the encoder code to fix up the actual CRTC mode.
* Split the digital from the analog stuff, as they have almost no
common code and IMHO this will substantially improve consistency
once the TVDAC implementation is imported.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/Makefile | 4 +-
drivers/gpu/drm/nouveau/nouveau_bios.c | 26 +-
drivers/gpu/drm/nouveau/nouveau_drv.h | 33 ++-
drivers/gpu/drm/nouveau/nouveau_hw.c | 10 +
drivers/gpu/drm/nouveau/nv04_crtc.c | 262 ++---------
drivers/gpu/drm/nouveau/nv04_dac.c | 497 ++++++++++++++++++++
drivers/gpu/drm/nouveau/nv04_dfp.c | 611 ++++++++++++++++++++++++
drivers/gpu/drm/nouveau/nv04_display.c | 29 +-
drivers/gpu/drm/nouveau/nv04_output.c | 797 --------------------------------
drivers/gpu/drm/nouveau/nvreg.h | 14 +-
10 files changed, 1235 insertions(+), 1048 deletions(-)
create mode 100644 drivers/gpu/drm/nouveau/nv04_dac.c
create mode 100644 drivers/gpu/drm/nouveau/nv04_dfp.c
delete mode 100644 drivers/gpu/drm/nouveau/nv04_output.c
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 2ba1e45..4965e00 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -18,8 +18,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o
nouveau_mem.o \
nv04_instmem.o nv50_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
- nv04_display.o nv04_output.o nv04_crtc.o nv04_cursor.o \
- nv04_fbcon.o
+ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
+ nv04_dac.o nv04_dfp.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c
b/drivers/gpu/drm/nouveau/nouveau_bios.c
index db52f25..e9e1e8f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -2698,30 +2698,6 @@ static void parse_init_tables(struct drm_device *dev,
struct nvbios *bios)
}
}
-static void link_head_and_output(struct drm_device *dev, struct dcb_entry
*dcbent, int head, bool dl)
-{
- /* The BIOS scripts don't do this for us, sadly
- * Luckily we do know the values ;-)
- *
- * head < 0 indicates we wish to force a setting with the overrideval
- * (for VT restore etc.)
- */
-
- int ramdac = (dcbent->or & OUTPUT_C) >> 2;
- uint8_t tmds04 = 0x80;
-
- if (head != ramdac)
- tmds04 = 0x88;
-
- if (dcbent->type == OUTPUT_LVDS)
- tmds04 |= 0x01;
-
- nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
-
- if (dl) /* dual link */
- nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
-}
-
static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk)
{
int compare_record_len, i = 0;
@@ -2762,7 +2738,7 @@ static void run_digital_op_script(struct drm_device *dev,
uint16_t scriptptr, st
NVWriteVgaCrtc5758(dev, head, 0, dcbent->index);
parse_init_table(dev, bios, scriptptr, &iexec);
- link_head_and_output(dev, dcbent, head, dl);
+ nv04_dfp_bind_head(dev, dcbent, head, dl);
}
static int call_lvds_manufacturer_script(struct drm_device *dev, struct
dcb_entry *dcbent, int head, enum LVDS_script script)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 1197c68..22d08bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -331,6 +331,16 @@ struct nouveau_pll_vals {
int refclk;
};
+enum nv04_fp_display_regs {
+ FP_DISPLAY_END,
+ FP_TOTAL,
+ FP_CRTC,
+ FP_SYNC_START,
+ FP_SYNC_END,
+ FP_VALID_START,
+ FP_VALID_END
+};
+
struct nv04_crtc_reg {
unsigned char MiscOutReg; /* */
uint8_t CRTC[0x9f];
@@ -364,6 +374,8 @@ struct nv04_crtc_reg {
uint32_t fp_debug_0;
uint32_t fp_debug_1;
uint32_t fp_debug_2;
+ uint32_t fp_margin_color;
+ uint32_t ramdac_8c0;
uint32_t ramdac_a20;
uint32_t ramdac_a24;
uint32_t ramdac_a34;
@@ -525,6 +537,7 @@ struct drm_nouveau_private {
struct nv04_mode_state saved_reg;
uint32_t saved_vga_font[4][16384];
uint32_t crtc_owner;
+ uint32_t dac_users[4];
struct nouveau_suspend_resume {
uint32_t fifo_mode;
@@ -893,16 +906,26 @@ extern void nv04_timer_takedown(struct drm_device *);
extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
+/* nv04_dac.c */
+extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
+extern enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector);
+extern int nv04_dac_output_offset(struct drm_encoder *encoder);
+extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
+
+/* nv04_dfp.c */
+extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
+extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry
*dcbent);
+extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry
*dcbent,
+ int head, bool dl);
+extern void nv04_dfp_disable(struct drm_device *dev, int head);
+extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
+
/* nv04_display.c */
extern int nv04_display_create(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *);
extern void nv04_display_restore(struct drm_device *);
-/* nv04_output.c */
-extern int nv04_encoder_create(struct drm_device *, struct dcb_entry *);
-extern int nv04_connector_create(struct drm_device *, int i2c_index,
- uint16_t encoders, int type);
-
/* nv04_crtc.c */
extern int nv04_crtc_create(struct drm_device *, int index);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c
b/drivers/gpu/drm/nouveau/nouveau_hw.c
index d270d6f..5f49aed 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -698,6 +698,11 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1);
regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2);
+ regp->fp_margin_color = NVReadRAMDAC(dev, head,
NV_PRAMDAC_FP_MARGIN_COLOR);
+
+ if (nv_arch(dev) >= NV_30)
+ regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
+
if (nv_arch(dev) == NV_40) {
regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
@@ -751,6 +756,11 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR,
regp->fp_margin_color);
+
+ if (nv_arch(dev) >= NV_30)
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
+
if (nv_arch(dev) == NV_40) {
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c
b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 103e28c..802b290 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -74,6 +74,18 @@ static void nv_crtc_set_image_sharpening(struct drm_crtc
*crtc, int level)
NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634,
regp->ramdac_634);
}
+#define PLLSEL_VPLL1_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
+#define PLLSEL_VPLL2_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
+#define PLLSEL_TV_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
/* NV4x 0x40.. pll notes:
* gpu pll: 0x4000 + 0x4004
* ?gpu? pll: 0x4008 + 0x400c
@@ -122,17 +134,16 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc,
struct drm_display_mod
if (!(vclk = nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv)))
return;
+ state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK |
PLLSEL_TV_MASK;
+
/* The blob uses this always, so let's do the same */
if (nv_arch(dev) == NV_40)
- state->pllsel |= NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE;
+ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
/* again nv40 and some nv43 act more like nv3x as described above */
if (dev_priv->chipset < 0x41)
state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
- state->pllsel |= (nv_crtc->index ? NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2
|
- NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2 :
- NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL |
- NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2);
+ state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
if (pv->NM2)
NV_TRACE(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
@@ -218,8 +229,7 @@ nv_crtc_mode_fixup(struct drm_crtc *crtc, struct
drm_display_mode * mode,
}
static void
-nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
{
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -449,7 +459,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
struct nv04_crtc_reg *regp =
&dev_priv->mode_reg.crtc_reg[nv_crtc->index];
struct nv04_crtc_reg *savep =
&dev_priv->saved_reg.crtc_reg[nv_crtc->index];
struct drm_encoder *encoder;
- bool lvds_output = false, tmds_output = false, off_chip_digital = false;
+ bool lvds_output = false, tmds_output = false, tv_output = false,
+ off_chip_digital = false;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -460,6 +471,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
if (nv_encoder->dcb->type == OUTPUT_LVDS)
digital = lvds_output = true;
+ if (nv_encoder->dcb->type == OUTPUT_TV)
+ tv_output = true;
if (nv_encoder->dcb->type == OUTPUT_TMDS)
digital = tmds_output = true;
if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
@@ -550,7 +563,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
/* Enable slaved mode (called MODE_TV in nv4ref.h) */
- if (lvds_output || tmds_output)
+ if (lvds_output || tmds_output || tv_output)
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
/* Generic PRAMDAC regs */
@@ -572,187 +585,12 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
/* Some values the blob sets */
+ regp->ramdac_8c0 = 0x100;
regp->ramdac_a20 = 0x0;
regp->ramdac_a24 = 0xfffff;
regp->ramdac_a34 = 0x1;
}
-enum fp_display_regs {
- FP_DISPLAY_END,
- FP_TOTAL,
- FP_CRTC,
- FP_SYNC_START,
- FP_SYNC_END,
- FP_VALID_START,
- FP_VALID_END
-};
-
-/* this could be set in nv_output, but would require some rework of load/save
*/
-static void
-nv_crtc_mode_set_fp_regs(struct drm_crtc *crtc, struct drm_display_mode *mode,
- struct drm_display_mode * adjusted_mode)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- struct nv04_crtc_reg *regp =
&dev_priv->mode_reg.crtc_reg[nv_crtc->index];
- struct nv04_crtc_reg *savep =
&dev_priv->saved_reg.crtc_reg[nv_crtc->index];
- struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
- struct nouveau_encoder *nv_encoder = NULL;
- struct drm_encoder *encoder;
- uint32_t mode_ratio, panel_ratio;
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- /* assuming one fp output per crtc seems ok */
- if (encoder->crtc != crtc)
- continue;
- nv_encoder = nouveau_encoder(encoder);
-
- if (nv_encoder->dcb->type == OUTPUT_LVDS ||
- nv_encoder->dcb->type == OUTPUT_TMDS)
- break;
- nv_encoder = NULL;
- }
-
- if (!nv_encoder)
- return;
-
- regp->fp_horiz_regs[FP_DISPLAY_END] = adjusted_mode->hdisplay - 1;
- regp->fp_horiz_regs[FP_TOTAL] = adjusted_mode->htotal - 1;
- if (!nv_gf4_disp_arch(dev) ||
- (adjusted_mode->hsync_start - adjusted_mode->hdisplay) >-
dev_priv->vbios->digital_min_front_porch)
- regp->fp_horiz_regs[FP_CRTC] = adjusted_mode->hdisplay;
- else
- regp->fp_horiz_regs[FP_CRTC] = adjusted_mode->hsync_start -
dev_priv->vbios->digital_min_front_porch - 1;
- regp->fp_horiz_regs[FP_SYNC_START] = adjusted_mode->hsync_start - 1;
- regp->fp_horiz_regs[FP_SYNC_END] = adjusted_mode->hsync_end - 1;
- regp->fp_horiz_regs[FP_VALID_START] = adjusted_mode->hskew;
- regp->fp_horiz_regs[FP_VALID_END] = adjusted_mode->hdisplay - 1;
-
- regp->fp_vert_regs[FP_DISPLAY_END] = adjusted_mode->vdisplay - 1;
- regp->fp_vert_regs[FP_TOTAL] = adjusted_mode->vtotal - 1;
- regp->fp_vert_regs[FP_CRTC] = adjusted_mode->vtotal - 5 - 1;
- regp->fp_vert_regs[FP_SYNC_START] = adjusted_mode->vsync_start - 1;
- regp->fp_vert_regs[FP_SYNC_END] = adjusted_mode->vsync_end - 1;
- regp->fp_vert_regs[FP_VALID_START] = 0;
- regp->fp_vert_regs[FP_VALID_END] = adjusted_mode->vdisplay - 1;
-
- /* bit26: a bit seen on some g7x, no as yet discernable purpose */
- regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
- (savep->fp_control & (1 << 26 |
NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
- /* Deal with vsync/hsync polarity */
- /* LVDS screens do set this, but modes with +ve syncs are very rare */
- if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
- if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
- /* panel scaling first, as native would get set otherwise */
- if (nv_connector->scaling_mode == DRM_MODE_SCALE_NON_GPU ||
- nv_connector->scaling_mode == DRM_MODE_SCALE_NO_SCALE) /* panel handles
it */
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
- else if (mode->hdisplay == adjusted_mode->hdisplay &&
- mode->vdisplay == adjusted_mode->vdisplay) /* native mode */
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
- else /* gpu needs to scale */
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
- if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
- regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
- if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
- nv_connector->native_mode->clock > 165000)
- regp->fp_control |= (2 << 24);
- if (nv_encoder->dcb->type == OUTPUT_LVDS) {
- bool duallink, dummy;
-
- nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
- clock, &duallink, &dummy);
- if (duallink)
- regp->fp_control |= (8 << 28);
- } else
- if (nv_connector->native_mode->clock > 165000)
- regp->fp_control |= (8 << 28);
-
- regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
- NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
- NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
- NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
- NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
- NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
- NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
-
- /* We want automatic scaling */
- regp->fp_debug_1 = 0;
- /* This can override HTOTAL and VTOTAL */
- regp->fp_debug_2 = 0;
-
- /* Use 20.12 fixed point format to avoid floats */
- mode_ratio = (1 << 12) * mode->hdisplay / mode->vdisplay;
- panel_ratio = (1 << 12) * adjusted_mode->hdisplay /
adjusted_mode->vdisplay;
- /* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
- * get treated the same as SCALE_FULLSCREEN */
- if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
- mode_ratio != panel_ratio) {
- uint32_t diff, scale;
- bool divide_by_2 = nv_gf4_disp_arch(dev);
-
- if (mode_ratio < panel_ratio) {
- /* vertical needs to expand to glass size (automatic)
- * horizontal needs to be scaled at vertical scale factor
- * to maintain aspect */
-
- scale = (1 << 12) * mode->vdisplay / adjusted_mode->vdisplay;
- regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
- XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
-
- /* restrict area of screen used, horizontally */
- diff = adjusted_mode->hdisplay -
- adjusted_mode->vdisplay * mode_ratio / (1 << 12);
- regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
- regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
- }
-
- if (mode_ratio > panel_ratio) {
- /* horizontal needs to expand to glass size (automatic)
- * vertical needs to be scaled at horizontal scale factor
- * to maintain aspect */
-
- scale = (1 << 12) * mode->hdisplay / adjusted_mode->hdisplay;
- regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
- XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
-
- /* restrict area of screen used, vertically */
- diff = adjusted_mode->vdisplay -
- (1 << 12) * adjusted_mode->hdisplay / mode_ratio;
- regp->fp_vert_regs[FP_VALID_START] += diff / 2;
- regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
- }
- }
-
- /* Output property. */
- if (nv_connector && nv_connector->use_dithering) {
- if (dev_priv->chipset == 0x11)
- regp->dither = savep->dither | 0x00010000;
- else {
- int i;
- regp->dither = savep->dither | 0x00000001;
- for (i = 0; i < 3; i++) {
- regp->dither_regs[i] = 0xe4e4e4e4;
- regp->dither_regs[i + 3] = 0x44444444;
- }
- }
- } else {
- if (dev_priv->chipset != 0x11) {
- /* reset them */
- int i;
- for (i = 0; i < 3; i++) {
- regp->dither_regs[i] = savep->dither_regs[i];
- regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
- }
- }
- regp->dither = savep->dither;
- }
-}
-
/**
* Sets up registers for the given mode/adjusted_mode pair.
*
@@ -770,35 +608,18 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct
drm_display_mode *mode,
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_nouveau_private *dev_priv = dev->dev_private;
- NV_TRACE(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
- drm_mode_debug_printmodeline(mode);
- NV_TRACE(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
- drm_mode_debug_printmodeline(mode);
+ NV_DEBUG(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
+ drm_mode_debug_printmodeline(adjusted_mode);
/* unlock must come after turning off FP_TG_CONTROL in output_prepare */
nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
- nv_crtc_mode_set_vga(crtc, mode, adjusted_mode);
- /* calculated in output_prepare, nv40 needs it written before calculating PLLs
*/
+ nv_crtc_mode_set_vga(crtc, adjusted_mode);
+ /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating
PLLs */
if (nv_arch(dev) == NV_40)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
- nv_crtc_mode_set_regs(crtc, mode);
- nv_crtc_mode_set_fp_regs(crtc, mode, adjusted_mode);
+ nv_crtc_mode_set_regs(crtc, adjusted_mode);
nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
-
- nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
-
- nv04_crtc_mode_set_base(crtc, x, y, NULL);
-
-#ifdef __BIG_ENDIAN
- /* turn on LFB swapping */
- {
- uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
- tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
- NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
- }
-#endif
-
return 0;
}
@@ -806,15 +627,21 @@ static void nv_crtc_save(struct drm_crtc *crtc)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
+ struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
+ struct nv04_mode_state *saved = &dev_priv->saved_reg;
+ struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
if (nv_two_heads(crtc->dev))
NVSetOwner(crtc->dev, nv_crtc->index);
- nouveau_hw_save_state(crtc->dev, nv_crtc->index,
&dev_priv->saved_reg);
+ nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
/* init some state to saved value */
- dev_priv->mode_reg.sel_clk = dev_priv->saved_reg.sel_clk & ~(0x5
<< 16);
- dev_priv->mode_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX]
=
dev_priv->saved_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX];
+ state->sel_clk = saved->sel_clk & ~(0x5 << 16);
+ crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] =
crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
+ state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK |
PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
+ crtc_state->gpio_ext = crtc_saved->gpio_ext;
}
static void nv_crtc_restore(struct drm_crtc *crtc)
@@ -856,7 +683,22 @@ static void nv_crtc_prepare(struct drm_crtc *crtc)
static void nv_crtc_commit(struct drm_crtc *crtc)
{
+ struct drm_device *dev = crtc->dev;
struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+ nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
+ nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
+
+#ifdef __BIG_ENDIAN
+ /* turn on LFB swapping */
+ {
+ uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
+ tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
+ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
+ }
+#endif
funcs->dpms(crtc, DRM_MODE_DPMS_ON);
}
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c
b/drivers/gpu/drm/nouveau/nv04_dac.c
new file mode 100644
index 0000000..05fc393
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
"Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+int nv04_dac_output_offset(struct drm_encoder *encoder)
+{
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ int offset = 0;
+
+ if (dcb->or & (8 | OUTPUT_C))
+ offset += 0x68;
+ if (dcb->or & (8 | OUTPUT_B))
+ offset += 0x2000;
+
+ return offset;
+}
+
+/*
+ * arbitrary limit to number of sense oscillations tolerated in one sample
+ * period (observed to be at least 13 in "nvidia")
+ */
+#define MAX_HBLANK_OSC 20
+
+/*
+ * arbitrary limit to number of conflicting sample pairs to tolerate at a
+ * voltage step (observed to be at least 5 in "nvidia")
+ */
+#define MAX_SAMPLE_PAIRS 10
+
+static int sample_load_twice(struct drm_device *dev, bool sense[2])
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ bool sense_a, sense_b, sense_b_prime;
+ int j = 0;
+
+ /*
+ * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
+ * then wait for transition 0x4->0x5->0x4: enter hblank, leave
+ * hblank again
+ * use a 10ms timeout (guards against crtc being inactive, in
+ * which case blank state would never change)
+ */
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+ 0x00000001, 0x00000000))
+ return -EBUSY;
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+ 0x00000001, 0x00000001))
+ return -EBUSY;
+ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+ 0x00000001, 0x00000000))
+ return -EBUSY;
+
+ udelay(100);
+ /* when level triggers, sense is _LO_ */
+ sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+
+ /* take another reading until it agrees with sense_a... */
+ do {
+ udelay(100);
+ sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+ if (sense_a != sense_b) {
+ sense_b_prime + nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+ if (sense_b == sense_b_prime) {
+ /* ... unless two consecutive subsequent
+ * samples agree; sense_a is replaced */
+ sense_a = sense_b;
+ /* force mis-match so we loop */
+ sense_b = !sense_a;
+ }
+ }
+ } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
+
+ if (j == MAX_HBLANK_OSC)
+ /* with so much oscillation, default to sense:LO */
+ sense[i] = false;
+ else
+ sense[i] = sense_a;
+ }
+
+ return 0;
+}
+
+static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ uint8_t saved_seq1, saved_pi, saved_rpc1;
+ uint8_t saved_palette0[3], saved_palette_mask;
+ uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
+ int i;
+ uint8_t blue;
+ bool sense = true;
+
+ /*
+ * for this detection to work, there needs to be a mode set up on the
+ * CRTC. this is presumed to be the case
+ */
+
+ if (nv_two_heads(dev))
+ /* only implemented for head A for now */
+ NVSetOwner(dev, 0);
+
+ saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
+
+ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
+ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+ msleep(10);
+
+ saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
+ saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
+ saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
+
+ nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
+ for (i = 0; i < 3; i++)
+ saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
+ saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
+
+ saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
+ (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
+ NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
+ NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
+
+ blue = 8; /* start of test range */
+
+ do {
+ bool sense_pair[2];
+
+ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+ /* testing blue won't find monochrome monitors. I don't care */
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
+
+ i = 0;
+ /* take sample pairs until both samples in the pair agree */
+ do {
+ if (sample_load_twice(dev, sense_pair))
+ goto out;
+ } while ((sense_pair[0] != sense_pair[1]) &&
+ ++i < MAX_SAMPLE_PAIRS);
+
+ if (i == MAX_SAMPLE_PAIRS)
+ /* too much oscillation defaults to LO */
+ sense = false;
+ else
+ sense = sense_pair[0];
+
+ /*
+ * if sense goes LO before blue ramps to 0x18, monitor is not connected.
+ * ergo, if blue gets to 0x18, monitor must be connected
+ */
+ } while (++blue < 0x18 && sense);
+
+out:
+ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
+ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+ for (i = 0; i < 3; i++)
+ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
+
+ if (blue == 0x18) {
+ NV_TRACE(dev, "Load detected on head A\n");
+ return connector_status_connected;
+ }
+
+ return connector_status_disconnected;
+}
+
+enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
+ uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
+ saved_rtest_ctrl, temp, routput;
+ int head, present = 0;
+
+#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20)
+ testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+
+ if (dev_priv->vbios->dactestval)
+ testval = dev_priv->vbios->dactestval;
+
+ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
+ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+ saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
+
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
+ if (regoffset == 0x68) {
+ saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
+ }
+
+ msleep(4);
+
+ saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+ head = (saved_routput & 0x100) >> 8;
+#if 0
+ /* if there's a spare crtc, using it will minimise flicker for the case
+ * where the in-use crtc is in use by an off-chip tmds encoder */
+ if (xf86_config->crtc[head]->enabled &&
!xf86_config->crtc[head ^ 1]->enabled)
+ head ^= 1;
+#endif
+ /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
+ routput = (saved_routput & 0xfffffece) | head << 8;
+
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
+ msleep(1);
+
+ temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
+
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
+ NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
+ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+ temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+ msleep(5);
+
+ temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+
+ present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
+
+ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+ temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
+
+ /* bios does something more complex for restoring, but I think this is good
enough */
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
+ if (regoffset == 0x68)
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
+ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
+
+ if (present) {
+ NV_INFO(dev, "Load detected on output %c\n", '@' +
ffs(dcb->or));
+ return connector_status_connected;
+ }
+
+ return connector_status_disconnected;
+}
+
+
+static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void nv04_dac_prepare(struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ nv04_dfp_disable(dev, head);
+
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+ * at LCD__INDEX which we don't alter
+ */
+ if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
+ crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+}
+
+
+static void nv04_dac_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int head = nouveau_crtc(encoder->crtc)->index;
+
+ NV_TRACE(dev, "%s called for encoder %d\n", __func__,
+ nv_encoder->dcb->index);
+
+ if (nv_gf4_disp_arch(dev)) {
+ struct drm_encoder *rebind;
+ uint32_t dac_offset = nv04_dac_output_offset(encoder);
+ uint32_t otherdac;
+
+ /* bit 16-19 are bits that are set on some G70 cards,
+ * but don't seem to have much effect */
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+ head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+ /* force any other vga encoders to bind to the other crtc */
+ list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
+ if (rebind == encoder
+ || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
+ continue;
+
+ dac_offset = nv04_dac_output_offset(rebind);
+ otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+ (otherdac & ~0x0100) | (head ^ 1) << 8);
+ }
+ }
+
+ /* This could use refinement for flatpanels, but it should work this way */
+ if (dev_priv->chipset < 0x44)
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0xf0000000);
+ else
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0x00100000);
+}
+
+static void nv04_dac_commit(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+
+ if (nv_gf4_disp_arch(dev)) {
+ uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
+ int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
+ uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
+
+ if (enable) {
+ *dac_users |= 1 << dcb->index;
+ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+
+ } else if (!(*dac_users &= ~(1 << dcb->index)))
+ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk &
~NV_PRAMDAC_DACCLK_SEL_DACCLK);
+ }
+}
+
+static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ if (nv_encoder->last_dpms == mode)
+ return;
+ nv_encoder->last_dpms = mode;
+
+ NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
+ mode, nv_encoder->dcb->index);
+
+ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+static void nv04_dac_save(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+
+ if (nv_gf4_disp_arch(dev))
+ nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
+ nv04_dac_output_offset(encoder));
+}
+
+static void nv04_dac_restore(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+
+ if (nv_gf4_disp_arch(dev))
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
+ nv_encoder->restore.output);
+
+ nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dac_destroy(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ NV_DEBUG(encoder->dev, "\n");
+
+ drm_encoder_cleanup(encoder);
+ kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
+ .dpms = nv04_dac_dpms,
+ .save = nv04_dac_save,
+ .restore = nv04_dac_restore,
+ .mode_fixup = nv04_dac_mode_fixup,
+ .prepare = nv04_dac_prepare,
+ .commit = nv04_dac_commit,
+ .mode_set = nv04_dac_mode_set,
+ .detect = nv04_dac_detect
+};
+
+static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
+ .dpms = nv04_dac_dpms,
+ .save = nv04_dac_save,
+ .restore = nv04_dac_restore,
+ .mode_fixup = nv04_dac_mode_fixup,
+ .prepare = nv04_dac_prepare,
+ .commit = nv04_dac_commit,
+ .mode_set = nv04_dac_mode_set,
+ .detect = nv17_dac_detect
+};
+
+static const struct drm_encoder_funcs nv04_dac_funcs = {
+ .destroy = nv04_dac_destroy,
+};
+
+int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+ const struct drm_encoder_helper_funcs *helper;
+ struct drm_encoder *encoder;
+ struct nouveau_encoder *nv_encoder = NULL;
+
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+
+ encoder = to_drm_encoder(nv_encoder);
+
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ if (nv_gf4_disp_arch(dev))
+ helper = &nv17_dac_helper_funcs;
+ else
+ helper = &nv04_dac_helper_funcs;
+
+ drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
+ drm_encoder_helper_add(encoder, helper);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c
b/drivers/gpu/drm/nouveau/nv04_dfp.c
new file mode 100644
index 0000000..7913e5f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
"Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
+ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
+ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
+#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \
+ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \
+ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
+
+static inline bool is_fpc_off(uint32_t fpc)
+{
+ return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) =+
FP_TG_CONTROL_OFF);
+}
+
+int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
+{
+ /* special case of nv_read_tmds to find crtc associated with an output.
+ * this does not give a correct answer for off-chip dvi, but there's no
+ * use for such an answer anyway
+ */
+ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+
+ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
+ NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
+ return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8)
>> 3) ^ ramdac;
+}
+
+void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+ int head, bool dl)
+{
+ /* The BIOS scripts don't do this for us, sadly
+ * Luckily we do know the values ;-)
+ *
+ * head < 0 indicates we wish to force a setting with the overrideval
+ * (for VT restore etc.)
+ */
+
+ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+ uint8_t tmds04 = 0x80;
+
+ if (head != ramdac)
+ tmds04 = 0x88;
+
+ if (dcbent->type == OUTPUT_LVDS)
+ tmds04 |= 0x01;
+
+ nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
+
+ if (dl) /* dual link */
+ nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
+}
+
+void nv04_dfp_disable(struct drm_device *dev, int head)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+ if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
+ FP_TG_CONTROL_ON) {
+ /* digital remnants must be cleaned before new crtc
+ * values programmed. delay is time for the vga stuff
+ * to realise it's in control again
+ */
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
+ FP_TG_CONTROL_OFF);
+ msleep(50);
+ }
+ /* don't inadvertently turn it on when state written later */
+ crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
+}
+
+void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ struct nouveau_crtc *nv_crtc;
+ uint32_t *fpc;
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ nv_crtc = nouveau_crtc(encoder->crtc);
+ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+ if (is_fpc_off(*fpc)) {
+ /* using saved value is ok, as (is_digital && dpms_on &&
+ * fp_control==OFF) is (at present) *only* true when
+ * fpc's most recent change was by below "off" code
+ */
+ *fpc = nv_crtc->dpms_saved_fp_control;
+ }
+
+ nv_crtc->fp_users |= 1 <<
nouveau_encoder(encoder)->dcb->index;
+ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+ } else {
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ nv_crtc = nouveau_crtc(crtc);
+ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+ nv_crtc->fp_users &= ~(1 <<
nouveau_encoder(encoder)->dcb->index);
+ if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
+ nv_crtc->dpms_saved_fp_control = *fpc;
+ /* cut the FP output */
+ *fpc &= ~FP_TG_CONTROL_ON;
+ *fpc |= FP_TG_CONTROL_OFF;
+ NVWriteRAMDAC(dev, nv_crtc->index,
+ NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+ }
+ }
+ }
+}
+
+static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_connector *nv_connector =
nouveau_encoder_connector_get(nv_encoder);
+
+ /* For internal panels and gpu scaling on DVI we need the native mode */
+ if (nv_connector->scaling_mode != DRM_MODE_SCALE_NON_GPU) {
+ nv_encoder->mode = *nv_connector->native_mode;
+ adjusted_mode->clock = nv_connector->native_mode->clock;
+ } else {
+ nv_encoder->mode = *adjusted_mode;
+ }
+
+ return true;
+}
+
+static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
+ struct nouveau_encoder *nv_encoder, int head)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
+ uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 :
0x40000;
+
+ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
+ return;
+
+ /* SEL_CLK is only used on the primary ramdac
+ * It toggles spread spectrum PLL output and sets the bindings of PLLs
+ * to heads on digital outputs
+ */
+ if (head)
+ state->sel_clk |= bits1618;
+ else
+ state->sel_clk &= ~bits1618;
+
+ /* nv30:
+ * bit 0 NVClk spread spectrum on/off
+ * bit 2 MemClk spread spectrum on/off
+ * bit 4 PixClk1 spread spectrum on/off toggle
+ * bit 6 PixClk2 spread spectrum on/off toggle
+ *
+ * nv40 (observations from bios behaviour and mmio traces):
+ * bits 4&6 as for nv30
+ * bits 5&7 head dependent as for bits 4&6, but do not appear with
4&6;
+ * maybe a different spread mode
+ * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST
scripts)
+ * The logic behind turning spread spectrum on/off in the first place,
+ * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp
table
+ * entry has the necessary info)
+ */
+ if (nv_encoder->dcb->type == OUTPUT_LVDS &&
dev_priv->saved_reg.sel_clk & 0xf0) {
+ int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
+
+ state->sel_clk &= ~0xf0;
+ state->sel_clk |= (head ? 0x40 : 0x10) << shift;
+ }
+}
+
+static void nv04_dfp_prepare(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+ uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
+ uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
+
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
+
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+ * at LCD__INDEX which we don't alter
+ */
+ if (!(*cr_lcd & 0x44)) {
+ *cr_lcd = 0x3;
+
+ if (nv_two_heads(dev)) {
+ if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
+ *cr_lcd |= head ? 0x0 : 0x8;
+ else {
+ *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
+ if (nv_encoder->dcb->type == OUTPUT_LVDS)
+ *cr_lcd |= 0x30;
+ if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
+ /* avoid being connected to both crtcs */
+ *cr_lcd_oth &= ~0x30;
+ NVWriteVgaCrtc(dev, head ^ 1,
+ NV_CIO_CRE_LCD__INDEX,
+ *cr_lcd_oth);
+ }
+ }
+ }
+ }
+}
+
+
+static void nv04_dfp_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nv04_crtc_reg *regp =
&dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+ struct nv04_crtc_reg *savep =
&dev_priv->saved_reg.crtc_reg[nv_crtc->index];
+ struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_display_mode *output_mode = &nv_encoder->mode;
+ uint32_t mode_ratio, panel_ratio;
+
+ NV_DEBUG(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
+ drm_mode_debug_printmodeline(output_mode);
+
+ /* Initialize the FP registers in this CRTC. */
+ regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
+ regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+ if (!nv_gf4_disp_arch(dev) ||
+ (output_mode->hsync_start - output_mode->hdisplay) >+
dev_priv->vbios->digital_min_front_porch)
+ regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
+ else
+ regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start -
dev_priv->vbios->digital_min_front_porch - 1;
+ regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
+ regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+ regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
+ regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
+
+ regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
+ regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
+ regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
+ regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
+ regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
+ regp->fp_vert_regs[FP_VALID_START] = 0;
+ regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
+
+ /* bit26: a bit seen on some g7x, no as yet discernable purpose */
+ regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
+ (savep->fp_control & (1 << 26 |
NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
+ /* Deal with vsync/hsync polarity */
+ /* LVDS screens do set this, but modes with +ve syncs are very rare */
+ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
+ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
+ /* panel scaling first, as native would get set otherwise */
+ if (nv_connector->scaling_mode == DRM_MODE_SCALE_NON_GPU ||
+ nv_connector->scaling_mode == DRM_MODE_SCALE_NO_SCALE) /* panel handles
it */
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
+ else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
+ adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
+ else /* gpu needs to scale */
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
+ if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
+ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
+ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
+ nv_connector->native_mode->clock > 165000)
+ regp->fp_control |= (2 << 24);
+ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+ bool duallink, dummy;
+
+ nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
+ clock, &duallink, &dummy);
+ if (duallink)
+ regp->fp_control |= (8 << 28);
+ } else
+ if (nv_connector->native_mode->clock > 165000)
+ regp->fp_control |= (8 << 28);
+
+ regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
+ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
+ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
+ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
+ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
+ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
+ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
+
+ /* We want automatic scaling */
+ regp->fp_debug_1 = 0;
+ /* This can override HTOTAL and VTOTAL */
+ regp->fp_debug_2 = 0;
+
+ /* Use 20.12 fixed point format to avoid floats */
+ mode_ratio = (1 << 12) * adjusted_mode->hdisplay /
adjusted_mode->vdisplay;
+ panel_ratio = (1 << 12) * output_mode->hdisplay /
output_mode->vdisplay;
+ /* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
+ * get treated the same as SCALE_FULLSCREEN */
+ if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
+ mode_ratio != panel_ratio) {
+ uint32_t diff, scale;
+ bool divide_by_2 = nv_gf4_disp_arch(dev);
+
+ if (mode_ratio < panel_ratio) {
+ /* vertical needs to expand to glass size (automatic)
+ * horizontal needs to be scaled at vertical scale factor
+ * to maintain aspect */
+
+ scale = (1 << 12) * adjusted_mode->vdisplay /
output_mode->vdisplay;
+ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
+ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
+
+ /* restrict area of screen used, horizontally */
+ diff = output_mode->hdisplay -
+ output_mode->vdisplay * mode_ratio / (1 << 12);
+ regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
+ regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
+ }
+
+ if (mode_ratio > panel_ratio) {
+ /* horizontal needs to expand to glass size (automatic)
+ * vertical needs to be scaled at horizontal scale factor
+ * to maintain aspect */
+
+ scale = (1 << 12) * adjusted_mode->hdisplay /
output_mode->hdisplay;
+ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
+ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
+
+ /* restrict area of screen used, vertically */
+ diff = output_mode->vdisplay -
+ (1 << 12) * output_mode->hdisplay / mode_ratio;
+ regp->fp_vert_regs[FP_VALID_START] += diff / 2;
+ regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
+ }
+ }
+
+ /* Output property. */
+ if (nv_connector->use_dithering) {
+ if (dev_priv->chipset == 0x11)
+ regp->dither = savep->dither | 0x00010000;
+ else {
+ int i;
+ regp->dither = savep->dither | 0x00000001;
+ for (i = 0; i < 3; i++) {
+ regp->dither_regs[i] = 0xe4e4e4e4;
+ regp->dither_regs[i + 3] = 0x44444444;
+ }
+ }
+ } else {
+ if (dev_priv->chipset != 0x11) {
+ /* reset them */
+ int i;
+ for (i = 0; i < 3; i++) {
+ regp->dither_regs[i] = savep->dither_regs[i];
+ regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
+ }
+ }
+ regp->dither = savep->dither;
+ }
+
+ regp->fp_margin_color = 0;
+}
+
+static void nv04_dfp_commit(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct dcb_entry *dcbe = nv_encoder->dcb;
+ int head = nouveau_crtc(encoder->crtc)->index;
+
+ NV_TRACE(dev, "%s called for encoder %d\n", __func__,
nv_encoder->dcb->index);
+
+ if (dcbe->type == OUTPUT_TMDS)
+ run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
+ else if (dcbe->type == OUTPUT_LVDS)
+ call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
+
+ /* update fp_control state for any changes made by scripts,
+ * so correct value is written at DPMS on */
+ dev_priv->mode_reg.crtc_reg[head].fp_control + NVReadRAMDAC(dev, head,
NV_PRAMDAC_FP_TG_CONTROL);
+
+ /* This could use refinement for flatpanels, but it should work this way */
+ if (dev_priv->chipset < 0x44)
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0xf0000000);
+ else
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0x00100000);
+
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+static inline bool is_powersaving_dpms(int mode)
+{
+ return (mode != DRM_MODE_DPMS_ON);
+}
+
+static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
+
+ if (nv_encoder->last_dpms == mode)
+ return;
+ nv_encoder->last_dpms = mode;
+
+ NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
+ mode, nv_encoder->dcb->index);
+
+ if (was_powersaving && is_powersaving_dpms(mode))
+ return;
+
+ if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
+ struct nouveau_connector *nv_connector =
nouveau_encoder_connector_get(nv_encoder);
+
+ /* when removing an output, crtc may not be set, but PANEL_OFF
+ * must still be run
+ */
+ int head = crtc ? nouveau_crtc(crtc)->index :
+ nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ call_lvds_script(dev, nv_encoder->dcb, head,
+ LVDS_PANEL_ON, nv_connector->native_mode->clock);
+ else
+ /* pxclk of 0 is fine for PANEL_OFF, and for a
+ * disconnected LVDS encoder there is no native_mode
+ */
+ call_lvds_script(dev, nv_encoder->dcb, head,
+ LVDS_PANEL_OFF, 0);
+ }
+
+ nv04_dfp_update_fp_control(encoder, mode);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
+ else {
+ dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
+ dev_priv->mode_reg.sel_clk &= ~0xf0;
+ }
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
+}
+
+static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ if (nv_encoder->last_dpms == mode)
+ return;
+ nv_encoder->last_dpms = mode;
+
+ NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
+ mode, nv_encoder->dcb->index);
+
+ nv04_dfp_update_fp_control(encoder, mode);
+}
+
+static void nv04_dfp_save(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+
+ if (nv_two_heads(dev))
+ nv_encoder->restore.head + nv04_dfp_get_bound_head(dev,
nv_encoder->dcb);
+}
+
+static void nv04_dfp_restore(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int head = nv_encoder->restore.head;
+
+ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+ call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
+ nouveau_encoder_connector_get(nv_encoder)->native_mode->clock);
+
+ } else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
+ int clock = nouveau_hw_pllvals_to_clk
+ (&dev_priv->saved_reg.crtc_reg[head].pllvals);
+
+ run_tmds_table(dev, nv_encoder->dcb, head, clock);
+ }
+
+ nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dfp_destroy(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ NV_DEBUG(encoder->dev, "\n");
+
+ drm_encoder_cleanup(encoder);
+ kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
+ .dpms = nv04_lvds_dpms,
+ .save = nv04_dfp_save,
+ .restore = nv04_dfp_restore,
+ .mode_fixup = nv04_dfp_mode_fixup,
+ .prepare = nv04_dfp_prepare,
+ .commit = nv04_dfp_commit,
+ .mode_set = nv04_dfp_mode_set,
+ .detect = NULL,
+};
+
+static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
+ .dpms = nv04_tmds_dpms,
+ .save = nv04_dfp_save,
+ .restore = nv04_dfp_restore,
+ .mode_fixup = nv04_dfp_mode_fixup,
+ .prepare = nv04_dfp_prepare,
+ .commit = nv04_dfp_commit,
+ .mode_set = nv04_dfp_mode_set,
+ .detect = NULL,
+};
+
+static const struct drm_encoder_funcs nv04_dfp_funcs = {
+ .destroy = nv04_dfp_destroy,
+};
+
+int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+ const struct drm_encoder_helper_funcs *helper;
+ struct drm_encoder *encoder;
+ struct nouveau_encoder *nv_encoder = NULL;
+ int type;
+
+ switch (entry->type) {
+ case OUTPUT_TMDS:
+ type = DRM_MODE_ENCODER_TMDS;
+ helper = &nv04_tmds_helper_funcs;
+ break;
+ case OUTPUT_LVDS:
+ type = DRM_MODE_ENCODER_LVDS;
+ helper = &nv04_lvds_helper_funcs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+
+ encoder = to_drm_encoder(nv_encoder);
+
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
+ drm_encoder_helper_add(encoder, helper);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c
b/drivers/gpu/drm/nouveau/nv04_display.c
index ee50e34..4826e13 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -96,7 +96,7 @@ nv04_display_create(struct drm_device *dev)
struct drm_encoder *encoder;
struct drm_crtc *crtc;
uint16_t connectors[16] = { 0 };
- int i;
+ int i, ret;
NV_DEBUG(dev, "\n");
@@ -130,16 +130,25 @@ nv04_display_create(struct drm_device *dev)
for (i = 0; i < dcb->entries; i++) {
struct dcb_entry *dcbent = &dcb->entry[i];
- if (dcbent->type == OUTPUT_TV)
+ switch (dcbent->type) {
+ case OUTPUT_ANALOG:
+ ret = nv04_dac_create(dev, dcbent);
+ break;
+ case OUTPUT_LVDS:
+ case OUTPUT_TMDS:
+ ret = nv04_dfp_create(dev, dcbent);
+ break;
+ case OUTPUT_TV:
continue;
- if (dcbent->type > 3) {
+ default:
NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
continue;
}
- connectors[dcbent->i2c_index] |= 1 << i;
+ if (ret)
+ continue;
- nv04_encoder_create(dev, dcbent);
+ connectors[dcbent->i2c_index] |= 1 << i;
}
for (i = 0; i < dcb->entries; i++) {
@@ -171,13 +180,21 @@ nv04_display_create(struct drm_device *dev)
dev_priv->vbios->fp_no_ddc)
i2c_index = 0xf;
break;
+ case OUTPUT_TV:
+ type = DRM_MODE_CONNECTOR_TV;
+ break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
continue;
}
+ if (i2c_index == 0xf)
+ encoders = 1 << dcbent->index; /* allow multiple connectors with
the
+ same dummy i2c index */
+ else
+ connectors[i2c_index] = 0; /* avoid connectors being added multiply */
+
nouveau_connector_create(dev, i2c_index, type);
- connectors[i2c_index] = 0; /* avoid connectors being added multiply */
}
/* Save previous state */
diff --git a/drivers/gpu/drm/nouveau/nv04_output.c
b/drivers/gpu/drm/nouveau/nv04_output.c
deleted file mode 100644
index ba57dcf..0000000
--- a/drivers/gpu/drm/nouveau/nv04_output.c
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
- * Copyright 2003 NVIDIA, Corporation
- * Copyright 2006 Dave Airlie
- * Copyright 2007 Maarten Maathuis
- * Copyright 2007-2009 Stuart Bennett
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
"Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "drmP.h"
-#include "drm_crtc_helper.h"
-
-#include "nouveau_drv.h"
-#include "nouveau_encoder.h"
-#include "nouveau_connector.h"
-#include "nouveau_crtc.h"
-#include "nouveau_hw.h"
-#include "nvreg.h"
-
-static int
-nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder)
-{
- int offset = 0;
-
- if (nv_encoder->dcb->or & (8 | OUTPUT_C))
- offset += 0x68;
- if (nv_encoder->dcb->or & (8 | OUTPUT_B))
- offset += 0x2000;
-
- return offset;
-}
-
-static int
-nv_get_digital_bound_head(struct drm_device *dev, int or)
-{
- /* special case of nv_read_tmds to find crtc associated with an output.
- * this does not give a correct answer for off-chip dvi, but there's no
- * use for such an answer anyway
- */
- int ramdac = (or & OUTPUT_C) >> 2;
-
- NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
- NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
- return (((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8)
>> 3) ^ ramdac);
-}
-
-/*
- * arbitrary limit to number of sense oscillations tolerated in one sample
- * period (observed to be at least 13 in "nvidia")
- */
-#define MAX_HBLANK_OSC 20
-
-/*
- * arbitrary limit to number of conflicting sample pairs to tolerate at a
- * voltage step (observed to be at least 5 in "nvidia")
- */
-#define MAX_SAMPLE_PAIRS 10
-
-static int sample_load_twice(struct drm_device *dev, bool sense[2])
-{
- int i;
-
- for (i = 0; i < 2; i++) {
- bool sense_a, sense_b, sense_b_prime;
- int j = 0;
-
- /*
- * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
- * then wait for transition 0x4->0x5->0x4: enter hblank, leave
- * hblank again
- * use a 10ms timeout (guards against crtc being inactive, in
- * which case blank state would never change)
- */
- if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
- 0x00000001, 0x00000000))
- return -EBUSY;
- if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
- 0x00000001, 0x00000001))
- return -EBUSY;
- if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
- 0x00000001, 0x00000000))
- return -EBUSY;
-
- udelay(100);
- /* when level triggers, sense is _LO_ */
- sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
-
- /* take another reading until it agrees with sense_a... */
- do {
- udelay(100);
- sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
- if (sense_a != sense_b) {
- sense_b_prime - nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
- if (sense_b == sense_b_prime) {
- /* ... unless two consecutive subsequent
- * samples agree; sense_a is replaced */
- sense_a = sense_b;
- /* force mis-match so we loop */
- sense_b = !sense_a;
- }
- }
- } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
-
- if (j == MAX_HBLANK_OSC)
- /* with so much oscillation, default to sense:LO */
- sense[i] = false;
- else
- sense[i] = sense_a;
- }
-
- return 0;
-}
-
-static enum drm_connector_status
-nv04_dac_load_detect(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct drm_device *dev = encoder->dev;
- uint8_t saved_seq1, saved_pi, saved_rpc1;
- uint8_t saved_palette0[3], saved_palette_mask;
- uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
- int i;
- uint8_t blue;
- bool sense = true;
-
- /*
- * for this detection to work, there needs to be a mode set up on the
- * CRTC. this is presumed to be the case
- */
-
- if (nv_two_heads(dev))
- /* only implemented for head A for now */
- NVSetOwner(dev, 0);
-
- saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
- NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
-
- saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
- saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
-
- msleep(10);
-
- saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
- NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
- saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
- saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
- NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
-
- nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
- for (i = 0; i < 3; i++)
- saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
- saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
- nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
-
- saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
- (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
- NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
- NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
-
- blue = 8; /* start of test range */
-
- do {
- bool sense_pair[2];
-
- nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
- nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
- nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
- /* testing blue won't find monochrome monitors. I don't care */
- nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
-
- i = 0;
- /* take sample pairs until both samples in the pair agree */
- do {
- if (sample_load_twice(dev, sense_pair))
- goto out;
- } while ((sense_pair[0] != sense_pair[1]) &&
- ++i < MAX_SAMPLE_PAIRS);
-
- if (i == MAX_SAMPLE_PAIRS)
- /* too much oscillation defaults to LO */
- sense = false;
- else
- sense = sense_pair[0];
-
- /*
- * if sense goes LO before blue ramps to 0x18, monitor is not connected.
- * ergo, if blue gets to 0x18, monitor must be connected
- */
- } while (++blue < 0x18 && sense);
-
-out:
- nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
- nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
- for (i = 0; i < 3; i++)
- nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
- NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
- NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
- NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
-
- if (blue == 0x18) {
- NV_TRACE(dev, "Load detected on head A\n");
- return connector_status_connected;
- }
-
- return connector_status_disconnected;
-}
-
-static enum drm_connector_status
-nv17_dac_load_detect(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t testval, regoffset = nv_output_ramdac_offset(nv_encoder);
- uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
saved_rtest_ctrl, temp;
- int head, present = 0;
-
-#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20)
- testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
- if (dev_priv->vbios->dactestval)
- testval = dev_priv->vbios->dactestval;
-
- saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
- saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
-
- saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
-
- nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
- if (regoffset == 0x68) {
- saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
- nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
- }
-
- msleep(4);
-
- saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
- head = (saved_routput & 0x100) >> 8;
-#if 0
- /* if there's a spare crtc, using it will minimise flicker for the case
- * where the in-use crtc is in use by an off-chip tmds encoder */
- if (xf86_config->crtc[head]->enabled &&
!xf86_config->crtc[head ^ 1]->enabled)
- head ^= 1;
-#endif
- /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
- (saved_routput & 0xfffffece) | head << 8);
- msleep(1);
-
- temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
-
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
- NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
- temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
- temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
- msleep(1);
-
- present = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset) &
- NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
-
- temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
- temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
-
- /* bios does something more complex for restoring, but I think this is good
enough */
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
- if (regoffset == 0x68)
- nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
- nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
-
- if (present) {
- NV_INFO(dev, "Load detected on output %c\n",
- '@' + ffs(nv_encoder->dcb->or));
- return connector_status_connected;
- }
-
- return connector_status_disconnected;
-}
-
-static bool
-nv_output_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode
*mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_connector *nv_connector;
-
- nv_connector = nouveau_encoder_connector_get(nv_encoder);
- if (!nv_connector)
- return false;
-
- /* For internal panels and gpu scaling on DVI we need the native mode */
- if (nv_encoder->dcb->type == OUTPUT_LVDS ||
- (nv_encoder->dcb->type == OUTPUT_TMDS &&
nv_connector->scaling_mode != DRM_MODE_SCALE_NON_GPU)) {
- int id = adjusted_mode->base.id;
- *adjusted_mode = *nv_connector->native_mode;
- adjusted_mode->base.id = id;
- }
-
- return true;
-}
-
-static void
-nv_digital_output_prepare_sel_clk(struct drm_device *dev,
- struct nouveau_encoder *nv_encoder, int head)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nv04_mode_state *state = &dev_priv->mode_reg;
- uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 :
0x40000;
-
- if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
- return;
-
- /* SEL_CLK is only used on the primary ramdac
- * It toggles spread spectrum PLL output and sets the bindings of PLLs
- * to heads on digital outputs
- */
- if (head)
- state->sel_clk |= bits1618;
- else
- state->sel_clk &= ~bits1618;
-
- /* nv30:
- * bit 0 NVClk spread spectrum on/off
- * bit 2 MemClk spread spectrum on/off
- * bit 4 PixClk1 spread spectrum on/off toggle
- * bit 6 PixClk2 spread spectrum on/off toggle
- *
- * nv40 (observations from bios behaviour and mmio traces):
- * bits 4&6 as for nv30
- * bits 5&7 head dependent as for bits 4&6, but do not appear with
4&6;
- * maybe a different spread mode
- * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST
scripts)
- * The logic behind turning spread spectrum on/off in the first place,
- * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp
table
- * entry has the necessary info)
- */
- if (nv_encoder->dcb->type == OUTPUT_LVDS &&
dev_priv->saved_reg.sel_clk & 0xf0) {
- int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
-
- state->sel_clk &= ~0xf0;
- state->sel_clk |= (head ? 0x40 : 0x10) << shift;
- }
-}
-
-#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
- NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
- NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
-#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \
- NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \
- NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
-
-static inline bool is_fpc_off(uint32_t fpc)
-{
- return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) =-
FP_TG_CONTROL_OFF);
-}
-
-static void
-nv_output_prepare(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_encoder_helper_funcs *helper = encoder->helper_private;
- struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int head = nouveau_crtc(encoder->crtc)->index;
- struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
- uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
- uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
- bool digital_op = nv_encoder->dcb->type == OUTPUT_LVDS ||
- nv_encoder->dcb->type == OUTPUT_TMDS;
-
- helper->dpms(encoder, DRM_MODE_DPMS_OFF);
-
- if (nv_encoder->dcb->type == OUTPUT_ANALOG) {
- if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
- FP_TG_CONTROL_ON) {
- /* digital remnants must be cleaned before new crtc
- * values programmed. delay is time for the vga stuff
- * to realise it's in control again
- */
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
- FP_TG_CONTROL_OFF);
- msleep(50);
- }
- /* don't inadvertently turn it on when state written later */
- crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
- }
-
- /* calculate some output specific CRTC regs now, so that they can be
- * written in nv_crtc_set_mode
- */
-
- if (digital_op)
- nv_digital_output_prepare_sel_clk(dev, nv_encoder, head);
-
- /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
- * at LCD__INDEX which we don't alter
- */
- if (!(*cr_lcd & 0x44)) {
- *cr_lcd = digital_op ? 0x3 : 0x0;
- if (digital_op && nv_two_heads(dev)) {
- if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
- *cr_lcd |= head ? 0x0 : 0x8;
- else {
- *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
- if (nv_encoder->dcb->type == OUTPUT_LVDS)
- *cr_lcd |= 0x30;
- if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
- /* avoid being connected to both crtcs */
- *cr_lcd_oth &= ~0x30;
- NVWriteVgaCrtc(dev, head ^ 1,
- NV_CIO_CRE_LCD__INDEX,
- *cr_lcd_oth);
- }
- }
- }
- }
-}
-
-static void
-nv_output_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct dcb_entry *dcbe = nv_encoder->dcb;
- int head = nouveau_crtc(encoder->crtc)->index;
-
- NV_TRACE(dev, "%s called for encoder %d\n", __func__,
- nv_encoder->dcb->index);
-
- if (nv_gf4_disp_arch(dev) && dcbe->type == OUTPUT_ANALOG) {
- struct drm_encoder *rebind;
- uint32_t dac_offset = nv_output_ramdac_offset(nv_encoder);
- uint32_t otherdac;
-
- /* bit 16-19 are bits that are set on some G70 cards,
- * but don't seem to have much effect */
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
- head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
- /* force any other vga encoders to bind to the other crtc */
- list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
- struct nouveau_encoder *nv_rebind = nouveau_encoder(rebind);
-
- if (nv_rebind == nv_encoder || nv_rebind->dcb->type != OUTPUT_ANALOG)
- continue;
-
- dac_offset = nv_output_ramdac_offset(nv_rebind);
- otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
- (otherdac & ~0x0100) | (head ^ 1) << 8);
- }
- }
- if (dcbe->type == OUTPUT_TMDS)
- run_tmds_table(dev, dcbe, head, adjusted_mode->clock);
- else if (dcbe->type == OUTPUT_LVDS)
- call_lvds_script(dev, dcbe, head, LVDS_RESET, adjusted_mode->clock);
- if (dcbe->type == OUTPUT_LVDS || dcbe->type == OUTPUT_TMDS)
- /* update fp_control state for any changes made by scripts,
- * so correct value is written at DPMS on */
- dev_priv->mode_reg.crtc_reg[head].fp_control - NVReadRAMDAC(dev, head,
NV_PRAMDAC_FP_TG_CONTROL);
-
- /* This could use refinement for flatpanels, but it should work this way */
- if (dev_priv->chipset < 0x44)
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv_output_ramdac_offset(nv_encoder), 0xf0000000);
- else
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv_output_ramdac_offset(nv_encoder), 0x00100000);
-}
-
-static void
-nv_output_commit(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
- struct drm_encoder_helper_funcs *helper = encoder->helper_private;
-
- helper->dpms(encoder, DRM_MODE_DPMS_ON);
-
- NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
-
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
nv_crtc->index,
- '@' + ffs(nv_encoder->dcb->or));
-}
-
-static void
-dpms_update_fp_control(struct drm_device *dev,
- struct nouveau_encoder *nv_encoder, struct drm_crtc * crtc,
- int mode)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_crtc *nv_crtc;
- uint32_t *fpc;
-
- if (mode == DRM_MODE_DPMS_ON) {
- nv_crtc = nouveau_crtc(crtc);
- fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
-
- if (is_fpc_off(*fpc)) {
- /* using saved value is ok, as (is_digital && dpms_on &&
- * fp_control==OFF) is (at present) *only* true when
- * fpc's most recent change was by below "off" code
- */
- *fpc = nv_crtc->dpms_saved_fp_control;
- }
-
- nv_crtc->fp_users |= 1 << nv_encoder->dcb->index;
- NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
- } else {
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- nv_crtc = nouveau_crtc(crtc);
- fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
-
- nv_crtc->fp_users &= ~(1 << nv_encoder->dcb->index);
- if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
- nv_crtc->dpms_saved_fp_control = *fpc;
- /* cut the FP output */
- *fpc &= ~FP_TG_CONTROL_ON;
- *fpc |= FP_TG_CONTROL_OFF;
- NVWriteRAMDAC(dev, nv_crtc->index,
- NV_PRAMDAC_FP_TG_CONTROL, *fpc);
- }
- }
- }
-}
-
-static inline bool is_powersaving_dpms(int mode)
-{
- return (mode != DRM_MODE_DPMS_ON);
-}
-
-static void
-lvds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
-
- if (nv_encoder->last_dpms == mode)
- return;
- nv_encoder->last_dpms = mode;
-
- NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
- mode, nv_encoder->dcb->index);
-
- if (was_powersaving && is_powersaving_dpms(mode))
- return;
-
- if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
- struct nouveau_connector *nv_connector =
nouveau_encoder_connector_get(nv_encoder);
-
- /* when removing an output, crtc may not be set, but PANEL_OFF
- * must still be run
- */
- int head = crtc ? nouveau_crtc(crtc)->index :
- nv_get_digital_bound_head(dev, nv_encoder->dcb->or);
-
- if (mode == DRM_MODE_DPMS_ON)
- call_lvds_script(dev, nv_encoder->dcb, head,
- LVDS_PANEL_ON, nv_connector->native_mode->clock);
- else
- /* pxclk of 0 is fine for PANEL_OFF, and for a
- * disconnected LVDS encoder there is no native_mode
- */
- call_lvds_script(dev, nv_encoder->dcb, head,
- LVDS_PANEL_OFF, 0);
- }
-
- dpms_update_fp_control(dev, nv_encoder, crtc, mode);
-
- if (mode == DRM_MODE_DPMS_ON)
- nv_digital_output_prepare_sel_clk(dev, nv_encoder,
nouveau_crtc(crtc)->index);
- else {
- dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
- dev_priv->mode_reg.sel_clk &= ~0xf0;
- }
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
-}
-
-static void
-vga_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
-{
- if (nv_encoder->last_dpms == mode)
- return;
- nv_encoder->last_dpms = mode;
-
- NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
- mode, nv_encoder->dcb->index);
-
- if (nv_gf4_disp_arch(dev)) {
- uint32_t outputval = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
nv_output_ramdac_offset(nv_encoder));
-
- if (mode == DRM_MODE_DPMS_OFF)
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
nv_output_ramdac_offset(nv_encoder),
- outputval & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
- else if (mode == DRM_MODE_DPMS_ON)
- NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
nv_output_ramdac_offset(nv_encoder),
- outputval | NV_PRAMDAC_DACCLK_SEL_DACCLK);
- }
-}
-
-static void
-tmds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
-{
- if (nv_encoder->last_dpms == mode)
- return;
- nv_encoder->last_dpms = mode;
-
- NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
- mode, nv_encoder->dcb->index);
-
- dpms_update_fp_control(dev, nv_encoder, crtc, mode);
-}
-
-static void
-nv_output_dpms(struct drm_encoder *encoder, int mode)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct drm_crtc *crtc = encoder->crtc;
- void (* const encoder_dpms[4])(struct drm_device *, struct nouveau_encoder *,
struct drm_crtc *, int) - /* index matches DCB type */
- { vga_encoder_dpms, NULL, tmds_encoder_dpms, lvds_encoder_dpms };
-
- encoder_dpms[nv_encoder->dcb->type](dev, nv_encoder, crtc, mode);
-}
-
-static void
-nv04_encoder_save(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
-
- if (!nv_encoder->dcb) /* uninitialised encoder */
- return;
-
- if (nv_gf4_disp_arch(dev) && nv_encoder->dcb->type ==
OUTPUT_ANALOG)
- nv_encoder->restore.output - NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
- nv_output_ramdac_offset(nv_encoder));
- if (nv_two_heads(dev) && (nv_encoder->dcb->type == OUTPUT_LVDS
||
- nv_encoder->dcb->type == OUTPUT_TMDS))
- nv_encoder->restore.head - nv_get_digital_bound_head(dev,
nv_encoder->dcb->or);
-}
-
-static void
-nv04_encoder_restore(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int head = nv_encoder->restore.head;
-
- if (!nv_encoder->dcb) /* uninitialised encoder */
- return;
-
- if (nv_gf4_disp_arch(dev) && nv_encoder->dcb->type ==
OUTPUT_ANALOG)
- NVWriteRAMDAC(dev, 0,
- NV_PRAMDAC_DACCLK + nv_output_ramdac_offset(nv_encoder),
- nv_encoder->restore.output);
- if (nv_encoder->dcb->type == OUTPUT_LVDS)
- call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
- nouveau_encoder_connector_get(nv_encoder)->native_mode->clock);
- if (nv_encoder->dcb->type == OUTPUT_TMDS) {
- int clock = nouveau_hw_pllvals_to_clk
- (&dev_priv->saved_reg.crtc_reg[head].pllvals);
-
- run_tmds_table(dev, nv_encoder->dcb, head, clock);
- }
-
- nv_encoder->last_dpms = NV_DPMS_CLEARED;
-}
-
-static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
- .dpms = nv_output_dpms,
- .save = nv04_encoder_save,
- .restore = nv04_encoder_restore,
- .mode_fixup = nv_output_mode_fixup,
- .prepare = nv_output_prepare,
- .commit = nv_output_commit,
- .mode_set = nv_output_mode_set,
- .detect = nv04_dac_load_detect
-};
-
-static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
- .dpms = nv_output_dpms,
- .save = nv04_encoder_save,
- .restore = nv04_encoder_restore,
- .mode_fixup = nv_output_mode_fixup,
- .prepare = nv_output_prepare,
- .commit = nv_output_commit,
- .mode_set = nv_output_mode_set,
- .detect = nv17_dac_load_detect
-};
-
-static const struct drm_encoder_helper_funcs nv04_encoder_helper_funcs = {
- .dpms = nv_output_dpms,
- .save = nv04_encoder_save,
- .restore = nv04_encoder_restore,
- .mode_fixup = nv_output_mode_fixup,
- .prepare = nv_output_prepare,
- .commit = nv_output_commit,
- .mode_set = nv_output_mode_set,
- .detect = NULL
-};
-
-static void
-nv04_encoder_destroy(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
- NV_DEBUG(encoder->dev, "\n");
-
- drm_encoder_cleanup(encoder);
- kfree(nv_encoder);
-}
-
-static const struct drm_encoder_funcs nv04_encoder_funcs = {
- .destroy = nv04_encoder_destroy,
-};
-
-int
-nv04_encoder_create(struct drm_device *dev, struct dcb_entry *entry)
-{
- const struct drm_encoder_helper_funcs *helper;
- struct nouveau_encoder *nv_encoder = NULL;
- int type;
-
- switch (entry->type) {
- case OUTPUT_TMDS:
- type = DRM_MODE_ENCODER_TMDS;
- break;
- case OUTPUT_LVDS:
- type = DRM_MODE_ENCODER_LVDS;
- break;
- case OUTPUT_ANALOG:
- type = DRM_MODE_ENCODER_DAC;
- break;
- default:
- return -EINVAL;
- }
-
- nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
- if (!nv_encoder)
- return -ENOMEM;
-
- nv_encoder->dcb = entry;
- nv_encoder->or = ffs(entry->or) - 1;
-
- if (entry->type == OUTPUT_ANALOG) {
- if (nv_gf4_disp_arch(dev))
- helper = &nv17_dac_helper_funcs;
- else
- helper = &nv04_dac_helper_funcs;
- } else
- helper = &nv04_encoder_helper_funcs;
-
- drm_encoder_init(dev, to_drm_encoder(nv_encoder), &nv04_encoder_funcs,
type);
- drm_encoder_helper_add(to_drm_encoder(nv_encoder), helper);
-
- to_drm_encoder(nv_encoder)->possible_crtcs = entry->heads;
- to_drm_encoder(nv_encoder)->possible_clones = 0;
-
- return 0;
-}
-
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
index 90623b0..bc960b9 100644
--- a/drivers/gpu/drm/nouveau/nvreg.h
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -316,13 +316,18 @@
# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4)
#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c
-# define NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE (4 << 0)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8)
-# define NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20)
# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28)
-# define NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2 (2 << 28)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28)
#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510
#define NV_RAMDAC_VPLL2 0x00680520
@@ -384,6 +389,7 @@
# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24)
# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28)
# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28)
+#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c
#define NV_PRAMDAC_850 0x00680850
#define NV_PRAMDAC_85C 0x0068085c
#define NV_PRAMDAC_FP_DEBUG_0 0x00680880
@@ -409,6 +415,8 @@
# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16)
#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4
+#define NV_PRAMDAC_8C0 0x006808c0
+
/* Some kind of switch */
#define NV_PRAMDAC_900 0x00680900
#define NV_PRAMDAC_A20 0x00680A20
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 06/10] drm/nouveau: Parse some more BIOS parameters needed for TV-out.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_bios.c | 21 +++++++++++++++++++--
drivers/gpu/drm/nouveau/nouveau_bios.h | 4 ++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c
b/drivers/gpu/drm/nouveau/nouveau_bios.c
index e9e1e8f..ec0614f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -33,6 +33,7 @@
#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */
#define LEGACY_I2C_CRT 0x80
#define LEGACY_I2C_PANEL 0x81
+#define LEGACY_I2C_TV 0x82
#define EDID1_LEN 128
@@ -3960,8 +3961,8 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev,
struct nvbios *bios, st
if (!daccmpoffset)
return 0;
- /* The first value in the table, following the header, is the comparison value
- * Purpose of subsequent values unknown -- TV load detection?
+ /* The first value in the table, following the header, is the comparison
value,
+ * the second entry is a comparison value for TV load detection.
*/
dacver = bios->data[daccmpoffset];
@@ -3974,6 +3975,7 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev,
struct nvbios *bios, st
}
bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
+ bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen +
4]);
return 0;
}
@@ -4561,6 +4563,15 @@ parse_dcb20_entry(struct drm_device *dev, struct
bios_parsed_dcb *bdcb,
}
break;
}
+ case OUTPUT_TV:
+ {
+ if (bdcb->version >= 0x30)
+ entry->tvconf.has_component_output = conf & (0x8 << 4);
+ else
+ entry->tvconf.has_component_output = false;
+
+ break;
+ }
case 0xe:
/* weird g80 mobile type that "nv" treats as a terminator */
bdcb->dcb.entries--;
@@ -4626,6 +4637,10 @@ parse_dcb15_entry(struct drm_device *dev, struct
parsed_dcb *dcb,
/* invent a DVI-A output, by copying the fields of the DVI-D
* output; reported to work by math_b on an NV20(!) */
fabricate_vga_output(dcb, entry->i2c_index, entry->heads);
+ break;
+ case OUTPUT_TV:
+ entry->tvconf.has_component_output = false;
+ break;
}
return true;
@@ -4835,6 +4850,8 @@ static void fixup_legacy_i2c(struct nvbios *bios)
dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt;
if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL)
dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel;
+ if (dcb->entry[i].i2c_index == LEGACY_I2C_TV)
+ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv;
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h
b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 24fc305..e187ba6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -50,6 +50,9 @@ struct dcb_entry {
bool use_straps_for_mode;
bool use_power_scripts;
} lvdsconf;
+ struct {
+ bool has_component_output;
+ } tvconf;
};
bool i2c_upper_default;
};
@@ -145,6 +148,7 @@ struct nouveau_bios_info {
uint8_t chip_version;
uint32_t dactestval;
+ uint32_t tvdactestval;
uint8_t digital_min_front_porch;
bool fp_no_ddc;
};
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 07/10] drm/nouveau: Add some new register defines needed for TV-out.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/nouveau_drv.h | 9 +++++
drivers/gpu/drm/nouveau/nouveau_hw.c | 54 ++++++++++++++++++++++++++++----
drivers/gpu/drm/nouveau/nv04_crtc.c | 3 +-
drivers/gpu/drm/nouveau/nvreg.h | 24 ++++++++++++++
4 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 22d08bc..b35df18 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -366,6 +366,14 @@ struct nv04_crtc_reg {
uint32_t ramdac_gen_ctrl;
uint32_t ramdac_630;
uint32_t ramdac_634;
+ uint32_t tv_setup;
+ uint32_t tv_vtotal;
+ uint32_t tv_vskew;
+ uint32_t tv_vsync_delay;
+ uint32_t tv_htotal;
+ uint32_t tv_hskew;
+ uint32_t tv_hsync_delay;
+ uint32_t tv_hsync_delay2;
uint32_t fp_horiz_regs[7];
uint32_t fp_vert_regs[7];
uint32_t dither;
@@ -379,6 +387,7 @@ struct nv04_crtc_reg {
uint32_t ramdac_a20;
uint32_t ramdac_a24;
uint32_t ramdac_a34;
+ uint32_t ctv_regs[38];
};
struct nv04_output_reg {
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c
b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 5f49aed..786a755 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -673,6 +673,15 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
if (dev_priv->chipset >= 0x30)
regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
+ regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
+ regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL);
+ regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW);
+ regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY);
+ regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL);
+ regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW);
+ regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY);
+ regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head,
NV_PRAMDAC_TV_HSYNC_DELAY2);
+
for (i = 0; i < 7; i++) {
uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg);
@@ -707,6 +716,10 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34);
+
+ for (i = 0; i < 38; i++)
+ regp->ctv_regs[i] = NVReadRAMDAC(dev, head,
+ NV_PRAMDAC_CTV + 4*i);
}
}
@@ -736,6 +749,15 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
if (dev_priv->chipset >= 0x30)
NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2,
regp->tv_hsync_delay2);
+
for (i = 0; i < 7; i++) {
uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
@@ -765,6 +787,10 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34);
+
+ for (i = 0; i < 38; i++)
+ NVWriteRAMDAC(dev, head,
+ NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]);
}
}
@@ -838,6 +864,7 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
if (nv_arch(dev) >= NV_30)
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
@@ -846,10 +873,13 @@ nv_save_state_ext(struct drm_device *dev, int head,
if (nv_arch(dev) >= NV_10) {
regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830);
regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834);
- if (nv_arch(dev) == NV_40) {
- regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
+
+ if (nv_arch(dev) >= NV_30)
regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT);
- }
+
+ if (nv_arch(dev) == NV_40)
+ regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
+
if (nv_two_heads(dev))
regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL);
regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG);
@@ -888,6 +918,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_crtc_reg * regp = &state->crtc_reg[head];
+ uint32_t reg900;
int i;
if (nv_arch(dev) >= NV_10) {
@@ -911,13 +942,14 @@ nv_load_state_ext(struct drm_device *dev, int head,
NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830);
NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834);
- if (nv_arch(dev) == NV_40) {
- NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
+
+ if (nv_arch(dev) >= NV_30)
NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext);
- }
if (nv_arch(dev) == NV_40) {
- uint32_t reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
+ NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
+
+ reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
if (regp->crtc_cfg == NV_PCRTC_CONFIG_START_ADDRESS_HSYNC)
NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000);
else
@@ -939,6 +971,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
if (nv_arch(dev) >= NV_30)
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
@@ -956,6 +989,13 @@ nv_load_state_ext(struct drm_device *dev, int head,
}
/* NV11 and NV20 stop at 0x52. */
if (nv_gf4_disp_arch(dev)) {
+ if (nv_arch(dev) == NV_10) {
+ /* Not waiting for vertical retrace before modifying
+ CRE_53/CRE_54 causes lockups. */
+ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
+ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0);
+ }
+
wr_cio_state(dev, head, regp, NV_CIO_CRE_53);
wr_cio_state(dev, head, regp, NV_CIO_CRE_54);
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c
b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 802b290..dfe9003 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -550,7 +550,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
/* This is what the blob does */
regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
- if (nv_arch(dev) == NV_40)
+ if (nv_arch(dev) >= NV_30)
regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
regp->crtc_cfg = NV_PCRTC_CONFIG_START_ADDRESS_HSYNC;
@@ -581,6 +581,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
+ regp->tv_setup = 0;
nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
index bc960b9..5998c35 100644
--- a/drivers/gpu/drm/nouveau/nvreg.h
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -50,6 +50,9 @@
#define NV_PPM_OFFSET 0x0000A000
#define NV_PPM_SIZE 0x00001000
+#define NV_PTV_OFFSET 0x0000D000
+#define NV_PTV_SIZE 0x00001000
+
#define NV_PRMVGA_OFFSET 0x000A0000
#define NV_PRMVGA_SIZE 0x00020000
@@ -117,6 +120,12 @@
#define NV_PFIFO_RAMHT 0x00002210
+#define NV_PTV_TV_INDEX 0x0000d220
+#define NV_PTV_TV_DATA 0x0000d224
+#define NV_PTV_HFILTER 0x0000d310
+#define NV_PTV_HFILTER2 0x0000d390
+#define NV_PTV_VFILTER 0x0000d510
+
#define NV_PRMVIO_MISC__WRITE 0x000c03c2
#define NV_PRMVIO_SRX 0x000c03c4
#define NV_PRMVIO_SR 0x000c03c5
@@ -288,11 +297,13 @@
# define NV_CIO_CRE_EBR_VDE_11 2:2
# define NV_CIO_CRE_EBR_VRS_11 4:4
# define NV_CIO_CRE_EBR_VBS_11 6:6
+# define NV_CIO_CRE_43 0x43
# define NV_CIO_CRE_44 0x44 /* head control */
# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */
# define NV_CIO_CRE_RCR 0x46
# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7
# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */
+# define NV_CIO_CRE_49 0x49
# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably
scratch 6 */
# define NV_CIO_CRE_TVOUT_LATENCY 0x52
# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */
@@ -361,6 +372,17 @@
#define NV_PRAMDAC_630 0x00680630
#define NV_PRAMDAC_634 0x00680634
+#define NV_PRAMDAC_TV_SETUP 0x00680700
+#define NV_PRAMDAC_TV_VTOTAL 0x00680720
+#define NV_PRAMDAC_TV_VSKEW 0x00680724
+#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728
+#define NV_PRAMDAC_TV_HTOTAL 0x0068072c
+#define NV_PRAMDAC_TV_HSKEW 0x00680730
+#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734
+#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738
+
+#define NV_PRAMDAC_TV_SETUP 0x00680700
+
#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800
#define NV_PRAMDAC_FP_VTOTAL 0x00680804
#define NV_PRAMDAC_FP_VCRTC 0x00680808
@@ -423,6 +445,8 @@
#define NV_PRAMDAC_A24 0x00680A24
#define NV_PRAMDAC_A34 0x00680A34
+#define NV_PRAMDAC_CTV 0x00680c00
+
/* names fabricated from NV_USER_DAC info */
#define NV_PRMDIO_PIXEL_MASK 0x006813c6
# define NV_PRMDIO_PIXEL_MASK_MASK 0xff
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 08/10] drm: Import driver for the ch7006 I2C TV encoder chip.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/Kconfig | 14 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/i2c/Makefile | 4 +
drivers/gpu/drm/i2c/ch7006_drv.c | 477 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i2c/ch7006_mode.c | 470 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i2c/ch7006_priv.h | 333 ++++++++++++++++++++++++++
include/drm/i2c/ch7006.h | 86 +++++++
7 files changed, 1385 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpu/drm/i2c/Makefile
create mode 100644 drivers/gpu/drm/i2c/ch7006_drv.c
create mode 100644 drivers/gpu/drm/i2c/ch7006_mode.c
create mode 100644 drivers/gpu/drm/i2c/ch7006_priv.h
create mode 100644 include/drm/i2c/ch7006.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 5ea10e5..49b21cc 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -173,3 +173,17 @@ config DRM_NOUVEAU_BACKLIGHT
Say Y here if you want to control the backlight of your display
(e.g. a laptop panel).
+menu "I2C encoder or helper chips"
+ depends on DRM
+
+config DRM_I2C_CH7006
+ tristate "Chrontel ch7006 TV encoder"
+ default m if DRM_NOUVEAU
+ help
+ Support for Chrontel ch7006 and similar TV encoders, found
+ on some nVidia video cards.
+
+ This driver is currently only useful if you're also using
+ the nouveau driver.
+
+endmenu
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index eeb20aa..f0ef455 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-y += i2c/
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
new file mode 100644
index 0000000..6d2abaf
--- /dev/null
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -0,0 +1,4 @@
+ccflags-y := -Iinclude/drm
+
+ch7006-y := ch7006_drv.o ch7006_mode.o
+obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
new file mode 100644
index 0000000..c2594a1
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+/* DRM encoder functions */
+
+static void ch7006_encoder_set_config(struct drm_encoder *encoder,
+ void *params)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ priv->params = params;
+}
+
+static void ch7006_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ drm_property_destroy(encoder->dev, priv->scale_property);
+
+ kfree(priv);
+ to_encoder_slave(encoder)->slave_priv = NULL;
+
+ drm_i2c_encoder_destroy(encoder);
+}
+
+static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+
+ ch7006_dbg(client, "\n");
+
+ if (mode == priv->last_dpms)
+ return;
+ priv->last_dpms = mode;
+
+ ch7006_setup_power_state(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POWER);
+}
+
+static void ch7006_encoder_save(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_state_save(client, &priv->saved_state);
+}
+
+static void ch7006_encoder_restore(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_state_load(client, &priv->saved_state);
+}
+
+static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ /* The ch7006 is painfully picky with the input timings so no
+ * custom modes for now... */
+
+ priv->mode = ch7006_lookup_mode(encoder, mode);
+
+ return !!priv->mode;
+}
+
+static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ if (ch7006_lookup_mode(encoder, mode))
+ return MODE_OK;
+ else
+ return MODE_BAD;
+}
+
+static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_encoder_params *params = priv->params;
+ struct ch7006_state *state = &priv->state;
+ uint8_t *regs = state->regs;
+ struct ch7006_mode *mode = priv->mode;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ int start_active;
+
+ ch7006_dbg(client, "\n");
+
+ regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
+ regs[CH7006_BWIDTH] = 0;
+ regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
+ params->input_format);
+
+ regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
+ | bitf(CH7006_CLKMODE_XCM, params->xcm)
+ | bitf(CH7006_CLKMODE_PCM, params->pcm);
+ if (params->clock_mode)
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
+ if (params->clock_edge)
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
+
+ start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start
& ~0x7);
+ regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
+ regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
+
+ regs[CH7006_INPUT_SYNC] = 0;
+ if (params->sync_direction)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
+ if (params->sync_encoding)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
+ if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
+ if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
+
+ regs[CH7006_DETECT] = 0;
+ regs[CH7006_BCLKOUT] = 0;
+
+ regs[CH7006_SUBC_INC3] = 0;
+ if (params->pout_level)
+ regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
+
+ regs[CH7006_SUBC_INC4] = 0;
+ if (params->active_detect)
+ regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
+
+ regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
+
+ ch7006_setup_levels(encoder);
+ ch7006_setup_subcarrier(encoder);
+ ch7006_setup_pll(encoder);
+ ch7006_setup_power_state(encoder);
+ ch7006_setup_properties(encoder);
+
+ ch7006_state_load(client, state);
+}
+
+static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder
*encoder,
+ struct drm_connector *connector)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ int det;
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_save_reg(client, state, CH7006_DETECT);
+ ch7006_save_reg(client, state, CH7006_POWER);
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
+
+ ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
+ bitfs(CH7006_POWER_LEVEL, NORMAL));
+ ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
+
+ ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
+
+ ch7006_write(client, CH7006_DETECT, 0);
+
+ det = ch7006_read(client, CH7006_DETECT);
+
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
+ ch7006_load_reg(client, state, CH7006_POWER);
+ ch7006_load_reg(client, state, CH7006_DETECT);
+
+ if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+ CH7006_DETECT_SVIDEO_C_TEST|
+ CH7006_DETECT_CVBS_TEST)) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
+ else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+ CH7006_DETECT_SVIDEO_C_TEST)) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
+ else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
+ else
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+ drm_connector_property_set_value(connector,
+ encoder->dev->mode_config.tv_subconnector_property,
+ priv->subconnector);
+
+ return priv->subconnector?
+ connector_status_connected
+ : connector_status_disconnected;
+}
+
+static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_mode *mode;
+ int n = 0;
+
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
+ if (~mode->valid_scales & 1<<priv->scale ||
+ ~mode->valid_norms & 1<<priv->norm)
+ continue;
+
+ drm_mode_probed_add(connector,
+ drm_mode_duplicate(encoder->dev, &mode->mode));
+
+ n++;
+ }
+
+ return n;
+}
+
+static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *conf = &dev->mode_config;
+
+ drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
+
+ priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "scale", 2);
+ priv->scale_property->values[0] = 0;
+ priv->scale_property->values[1] = 2;
+
+ drm_connector_attach_property(connector,
conf->tv_select_subconnector_property,
+ priv->select_subconnector);
+ drm_connector_attach_property(connector, conf->tv_subconnector_property,
+ priv->subconnector);
+ drm_connector_attach_property(connector, conf->tv_left_margin_property,
+ priv->hmargin);
+ drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
+ priv->vmargin);
+ drm_connector_attach_property(connector, conf->tv_mode_property,
+ priv->norm);
+ drm_connector_attach_property(connector, conf->tv_brightness_property,
+ priv->brightness);
+ drm_connector_attach_property(connector, conf->tv_contrast_property,
+ priv->contrast);
+ drm_connector_attach_property(connector,
conf->tv_flicker_reduction_property,
+ priv->flicker);
+ drm_connector_attach_property(connector, priv->scale_property,
+ priv->scale);
+
+ return 0;
+}
+
+static int ch7006_encoder_set_property(struct drm_encoder *encoder,
+ struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct drm_mode_config *conf = &encoder->dev->mode_config;
+
+ ch7006_dbg(client, "\n");
+
+ if (property == conf->tv_select_subconnector_property) {
+ priv->select_subconnector = val;
+
+ ch7006_setup_power_state(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POWER);
+
+ } else if (property == conf->tv_left_margin_property) {
+ priv->hmargin = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_HPOS);
+
+ } else if (property == conf->tv_bottom_margin_property) {
+ priv->vmargin = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_VPOS);
+
+ } else if (property == conf->tv_mode_property) {
+ priv->norm = val;
+
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+ } else if (property == conf->tv_brightness_property) {
+ priv->brightness = val;
+
+ ch7006_setup_levels(encoder);
+
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+
+ } else if (property == conf->tv_contrast_property) {
+ priv->contrast = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
+
+ } else if (property == conf->tv_flicker_reduction_property) {
+ priv->flicker = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_FFILTER);
+
+ } else if (property == priv->scale_property) {
+ priv->scale = val;
+
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
+ .set_config = ch7006_encoder_set_config,
+ .destroy = ch7006_encoder_destroy,
+ .dpms = ch7006_encoder_dpms,
+ .save = ch7006_encoder_save,
+ .restore = ch7006_encoder_restore,
+ .mode_fixup = ch7006_encoder_mode_fixup,
+ .mode_valid = ch7006_encoder_mode_valid,
+ .mode_set = ch7006_encoder_mode_set,
+ .detect = ch7006_encoder_detect,
+ .get_modes = ch7006_encoder_get_modes,
+ .create_resources = ch7006_encoder_create_resources,
+ .set_property = ch7006_encoder_set_property,
+};
+
+
+/* I2C driver functions */
+
+static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id
*id)
+{
+ uint8_t addr = CH7006_VERSION_ID;
+ uint8_t val;
+ int ret;
+
+ ch7006_dbg(client, "\n");
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, &val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ ch7006_info(client, "Detected version ID: %x\n", val);
+
+ return 0;
+
+fail:
+ ch7006_err(client, "Error %d reading version ID\n", ret);
+
+ return -ENODEV;
+}
+
+static int ch7006_remove(struct i2c_client *client)
+{
+ ch7006_dbg(client, "\n");
+
+ return 0;
+}
+
+static int ch7006_encoder_init(struct i2c_client *client,
+ struct drm_device *dev,
+ struct drm_encoder_slave *encoder)
+{
+ struct ch7006_priv *priv;
+
+ ch7006_dbg(client, "\n");
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ encoder->slave_priv = priv;
+ encoder->slave_funcs = &ch7006_encoder_funcs;
+
+ priv->norm = TV_NORM_PAL;
+ priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+ priv->scale = 1;
+ priv->contrast = 50;
+ priv->brightness = 50;
+ priv->flicker = 50;
+ priv->hmargin = 50;
+ priv->vmargin = 50;
+ priv->last_dpms = -1;
+
+ return 0;
+}
+
+static struct i2c_device_id ch7006_ids[] = {
+ { "ch7006", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ch7006_ids);
+
+static struct drm_i2c_encoder_driver ch7006_driver = {
+ .i2c_driver = {
+ .probe = ch7006_probe,
+ .remove = ch7006_remove,
+
+ .driver = {
+ .name = "ch7006",
+ },
+
+ .id_table = ch7006_ids,
+ },
+
+ .encoder_init = ch7006_encoder_init,
+};
+
+
+/* Module initialization */
+
+static int __init ch7006_init(void)
+{
+ return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
+}
+
+static void __exit ch7006_exit(void)
+{
+ drm_i2c_encoder_unregister(&ch7006_driver);
+}
+
+int ch7006_debug = 0;
+module_param_named(debug, ch7006_debug, int, 0600);
+MODULE_PARM_DESC(debug, "Enable debug output.");
+
+MODULE_AUTHOR("Francisco Jerez <currojerez at riseup.net>");
+MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
+MODULE_LICENSE("GPL and additional rights");
+
+module_init(ch7006_init);
+module_exit(ch7006_exit);
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c
b/drivers/gpu/drm/i2c/ch7006_mode.c
new file mode 100644
index 0000000..a4c2798
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+char *ch7006_tv_norm_names[] = {
+ [TV_NORM_PAL] = "PAL",
+ [TV_NORM_PAL_M] = "PAL-M",
+ [TV_NORM_PAL_N] = "PAL-N",
+ [TV_NORM_PAL_NC] = "PAL-Nc",
+ [TV_NORM_PAL_60] = "PAL-60",
+ [TV_NORM_NTSC_M] = "NTSC-M",
+ [TV_NORM_NTSC_J] = "NTSC-J",
+};
+
+#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
+ .vdisplay = 480, \
+ .vtotal = 525, \
+ .hvirtual = 660
+
+#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
+ .vdisplay = 576, \
+ .vtotal = 625, \
+ .hvirtual = 810
+
+struct ch7006_tv_norm_info ch7006_tv_norms[] = {
+ [TV_NORM_NTSC_M] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 3579545 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
+ .voffset = 0,
+ },
+ [TV_NORM_NTSC_J] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.286 * fixed1,
+ .subc_freq = 3579545 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_M] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 3575611.433 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+ .voffset = 16,
+ },
+
+ /* The following modes seem to work right but they're
+ * undocumented */
+
+ [TV_NORM_PAL_N] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_NC] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 3582056.25 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_60] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+ .voffset = 16,
+ },
+};
+
+#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
+ subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
+ .mode = { \
+ .name = #hd "x" #vd, \
+ .status = 0, \
+ .type = DRM_MODE_TYPE_DRIVER, \
+ .clock = f, \
+ .hdisplay = hd, \
+ .hsync_start = e_hd + 16, \
+ .hsync_end = e_hd + 80, \
+ .htotal = ht, \
+ .hskew = 0, \
+ .vdisplay = vd, \
+ .vsync_start = vd + 10, \
+ .vsync_end = vd + 26, \
+ .vtotal = vt, \
+ .vscan = 0, \
+ .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
+ DRM_MODE_FLAG_##vsynp##VSYNC, \
+ .vrefresh = 0, \
+ }, \
+ .enc_hdisp = e_hd, \
+ .enc_vdisp = e_vd, \
+ .subc_coeff = subc * fixed1, \
+ .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
+ bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
+ .valid_scales = scale_mask, \
+ .valid_norms = norm_mask \
+ }
+
+#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
+ subc, scale, scale_mask, norm_mask) \
+ __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
+ scale_mask, norm_mask, hd, vd)
+
+#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
+ 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
+
+#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1
<< TV_NORM_PAL_NC)
+
+struct ch7006_mode ch7006_modes[] = {
+ MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
+ MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
+ MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
+ MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
+ MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
+ MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
+ MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
+ MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
+ MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
+ MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
+ MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
+ MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
+ MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
+ MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
+ MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
+ MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
+ MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
+ MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
+ MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
+ __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE,
800, 600),
+ MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
+ MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
+ MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
+ MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
+ MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
+ {}
+};
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_mode *mode;
+
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
+
+ if (~mode->valid_norms & 1<<priv->norm)
+ continue;
+
+ if (mode->mode.hdisplay != drm_mode->hdisplay ||
+ mode->mode.vdisplay != drm_mode->vdisplay ||
+ mode->mode.vtotal != drm_mode->vtotal ||
+ mode->mode.htotal != drm_mode->htotal ||
+ mode->mode.clock != drm_mode->clock)
+ continue;
+
+ return mode;
+ }
+
+ return NULL;
+}
+
+/* Some common HW state calculation code */
+
+void ch7006_setup_levels(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *regs = priv->state.regs;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ fixed gain;
+ int black_level;
+
+ /* Set DAC_GAIN if the voltage drop between white and black is
+ * high enough. */
+ if (norm->black_level < 0.339 * fixed1) {
+ gain = 76*fixed1/71;
+
+ regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
+ } else {
+ gain = fixed1;
+
+ regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
+ }
+
+ black_level = norm->black_level*375/gain;
+
+ /* Correct it with the specified brightness. */
+ black_level = interpolate(90, black_level, 208, priv->brightness);
+
+ regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
+
+ ch7006_dbg(client, "black level: %d\n", black_level);
+}
+
+void ch7006_setup_subcarrier(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ struct ch7006_mode *mode = priv->mode;
+ uint32_t subc_inc;
+
+ subc_inc = ((mode->subc_coeff >> 8)
+ * (norm->subc_freq >> 24) + 0.5 * fixed1) / fixed1;
+
+ setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
+ setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
+ setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
+ setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
+ setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
+ setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
+ setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
+ setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
+
+ ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
+}
+
+void ch7006_setup_pll(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *regs = priv->state.regs;
+ struct ch7006_mode *mode = priv->mode;
+ int n, best_n = 0;
+ int m, best_m = 0;
+ int freq, best_freq = 0;
+
+ for (n = 0; n < CH7006_MAXN; n++) {
+ for (m = 0; m < CH7006_MAXM; m++) {
+ freq = CH7006_FREQ0*(n+2)/(m+2);
+
+ if (abs(freq - mode->mode.clock) <
+ abs(best_freq - mode->mode.clock)) {
+ best_freq = freq;
+ best_n = n;
+ best_m = m;
+ }
+ }
+ }
+
+ regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
+ bitf(CH7006_PLLOV_M_8, best_m);
+
+ regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
+ regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
+
+ if (best_n < 108)
+ regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
+ else
+ regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
+
+ ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
+ best_n, best_m, best_freq, best_n < 108);
+}
+
+void ch7006_setup_power_state(struct drm_encoder *encoder)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *power = &priv->state.regs[CH7006_POWER];
+ int subconnector;
+
+ subconnector = priv->select_subconnector?
+ priv->select_subconnector : priv->subconnector;
+
+ *power = CH7006_POWER_RESET;
+
+ if (priv->last_dpms == DRM_MODE_DPMS_ON) {
+ switch (subconnector) {
+ case DRM_MODE_SUBCONNECTOR_SVIDEO:
+ *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
+ break;
+ case DRM_MODE_SUBCONNECTOR_Composite:
+ *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
+ break;
+ case DRM_MODE_SUBCONNECTOR_SCART:
+ *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
+ CH7006_POWER_SCART;
+ break;
+ }
+
+ } else {
+ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ }
+}
+
+void ch7006_setup_properties(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ struct ch7006_mode *ch_mode = priv->mode;
+ struct drm_display_mode *mode = &ch_mode->mode;
+ uint8_t *regs = state->regs;
+ int flicker, contrast, hpos, vpos;
+ fixed scale, aspect;
+
+ flicker = interpolate(0, 2, 3, priv->flicker);
+ regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
+ bitf(CH7006_FFILTER_LUMA, flicker) |
+ bitf(CH7006_FFILTER_CHROMA, 1);
+
+ contrast = interpolate(0, 5, 7, priv->contrast);
+ regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
+
+ scale = norm->vtotal*fixed1/mode->vtotal;
+ aspect = ch_mode->enc_hdisp*fixed1/ch_mode->enc_vdisp;
+
+ hpos = (norm->hvirtual*aspect - mode->hdisplay*scale)
+ * priv->hmargin / scale / 100 / 4;
+
+ setbitf(state, CH7006_POV, HPOS_8, hpos);
+ setbitf(state, CH7006_HPOS, 0, hpos);
+
+ vpos = max(0LL, norm->vdisplay - mode->vdisplay*scale/fixed1
+ + norm->voffset) * priv->vmargin / 100 / 2;
+
+ setbitf(state, CH7006_POV, VPOS_8, vpos);
+ setbitf(state, CH7006_VPOS, 0, vpos);
+
+ ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
+}
+
+/* HW access functions */
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
+{
+ uint8_t buf[] = {addr, val};
+ int ret;
+
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ if (ret < 0)
+ ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
+ ret, addr);
+}
+
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
+{
+ uint8_t val;
+ int ret;
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, &val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ return val;
+
+fail:
+ ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
+ ret, addr);
+ return 0;
+}
+
+void ch7006_state_load(struct i2c_client *client,
+ struct ch7006_state *state)
+{
+ ch7006_load_reg(client, state, CH7006_POWER);
+
+ ch7006_load_reg(client, state, CH7006_DISPMODE);
+ ch7006_load_reg(client, state, CH7006_FFILTER);
+ ch7006_load_reg(client, state, CH7006_BWIDTH);
+ ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
+ ch7006_load_reg(client, state, CH7006_START_ACTIVE);
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+ ch7006_load_reg(client, state, CH7006_HPOS);
+ ch7006_load_reg(client, state, CH7006_VPOS);
+ ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
+ ch7006_load_reg(client, state, CH7006_DETECT);
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
+ ch7006_load_reg(client, state, CH7006_PLLOV);
+ ch7006_load_reg(client, state, CH7006_PLLM);
+ ch7006_load_reg(client, state, CH7006_PLLN);
+ ch7006_load_reg(client, state, CH7006_BCLKOUT);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC0);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC1);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC2);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC3);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC4);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC5);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC6);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC7);
+ ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
+ ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+ /* I don't know what this is for, but otherwise I get no
+ * signal.
+ */
+ ch7006_write(client, 0x3d, 0x0);
+}
+
+void ch7006_state_save(struct i2c_client *client,
+ struct ch7006_state *state)
+{
+ ch7006_save_reg(client, state, CH7006_POWER);
+
+ ch7006_save_reg(client, state, CH7006_DISPMODE);
+ ch7006_save_reg(client, state, CH7006_FFILTER);
+ ch7006_save_reg(client, state, CH7006_BWIDTH);
+ ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
+ ch7006_save_reg(client, state, CH7006_START_ACTIVE);
+ ch7006_save_reg(client, state, CH7006_POV);
+ ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
+ ch7006_save_reg(client, state, CH7006_HPOS);
+ ch7006_save_reg(client, state, CH7006_VPOS);
+ ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
+ ch7006_save_reg(client, state, CH7006_DETECT);
+ ch7006_save_reg(client, state, CH7006_CONTRAST);
+ ch7006_save_reg(client, state, CH7006_PLLOV);
+ ch7006_save_reg(client, state, CH7006_PLLM);
+ ch7006_save_reg(client, state, CH7006_PLLN);
+ ch7006_save_reg(client, state, CH7006_BCLKOUT);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC0);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC1);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC2);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC3);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC4);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC5);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC6);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC7);
+ ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
+ ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+ state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
+ (state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
+ (state->regs[CH7006_FFILTER] & 0x03) << 2;
+}
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h
b/drivers/gpu/drm/i2c/ch7006_priv.h
new file mode 100644
index 0000000..59945eb
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __DRM_I2C_CH7006_PRIV_H__
+#define __DRM_I2C_CH7006_PRIV_H__
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "drm_encoder_slave.h"
+#include "i2c/ch7006.h"
+
+typedef int64_t fixed;
+#define fixed1 (1LL << 32)
+
+enum ch7006_tv_norm {
+ TV_NORM_PAL,
+ TV_NORM_PAL_M,
+ TV_NORM_PAL_N,
+ TV_NORM_PAL_NC,
+ TV_NORM_PAL_60,
+ TV_NORM_NTSC_M,
+ TV_NORM_NTSC_J,
+ NUM_TV_NORMS
+};
+
+struct ch7006_tv_norm_info {
+ fixed vrefresh;
+ int vdisplay;
+ int vtotal;
+ int hvirtual;
+
+ fixed subc_freq;
+ fixed black_level;
+
+ uint32_t dispmode;
+ int voffset;
+};
+
+struct ch7006_mode {
+ struct drm_display_mode mode;
+
+ int enc_hdisp;
+ int enc_vdisp;
+
+ fixed subc_coeff;
+ uint32_t dispmode;
+
+ uint32_t valid_scales;
+ uint32_t valid_norms;
+};
+
+struct ch7006_state {
+ uint8_t regs[0x26];
+};
+
+struct ch7006_priv {
+ struct ch7006_encoder_params *params;
+ struct ch7006_mode *mode;
+
+ struct ch7006_state state;
+ struct ch7006_state saved_state;
+
+ struct drm_property *scale_property;
+
+ int select_subconnector;
+ int subconnector;
+ int hmargin;
+ int vmargin;
+ enum ch7006_tv_norm norm;
+ int brightness;
+ int contrast;
+ int flicker;
+ int scale;
+
+ int last_dpms;
+};
+
+#define to_ch7006_priv(x) ((struct ch7006_priv
*)to_encoder_slave(x)->slave_priv)
+
+extern int ch7006_debug;
+
+extern char *ch7006_tv_norm_names[];
+extern struct ch7006_tv_norm_info ch7006_tv_norms[];
+extern struct ch7006_mode ch7006_modes[];
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode);
+
+void ch7006_setup_levels(struct drm_encoder *encoder);
+void ch7006_setup_subcarrier(struct drm_encoder *encoder);
+void ch7006_setup_pll(struct drm_encoder *encoder);
+void ch7006_setup_power_state(struct drm_encoder *encoder);
+void ch7006_setup_properties(struct drm_encoder *encoder);
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
+
+void ch7006_state_load(struct i2c_client *client,
+ struct ch7006_state *state);
+void ch7006_state_save(struct i2c_client *client,
+ struct ch7006_state *state);
+
+/* Some helper macros */
+
+#define ch7006_dbg(client, format, ...) do { \
+ if (ch7006_debug) \
+ dev_printk(KERN_DEBUG, &client->dev, \
+ "%s: " format, __func__, ## __VA_ARGS__); \
+ } while (0)
+#define ch7006_info(client, format, ...) dev_info(&client->dev, format,
__VA_ARGS__)
+#define ch7006_err(client, format, ...) dev_err(&client->dev, format,
__VA_ARGS__)
+
+#define __mask(src, bitfield) (((2 << (1?bitfield)) - 1) & ~((1
<< (0?bitfield)) - 1))
+#define mask(bitfield) __mask(bitfield)
+
+#define __bitf(src, bitfield, x) (((x) >> (src) << (0?bitfield))
& \
+ __mask(src, bitfield))
+#define bitf(bitfield, x) __bitf(bitfield, x)
+#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
+#define setbitf(state, reg, bitfield, x) \
+ state->regs[reg] = (state->regs[reg] & \
+ (typeof(*state->regs)) ~mask(reg##_##bitfield)) | \
+ bitf(reg##_##bitfield, x)
+
+#define __unbitf(src, bitfield, x) ((x & __mask(src, bitfield)) \
+ >> (0?bitfield) << (src))
+#define unbitf(bitfield, x) __unbitf(bitfield, x)
+
+#define interpolate(y0, y1, y2, x) ((y1) + \
+ ((x) < 50 ? (y1) - (y0) : (y2) - (y1)) \
+ * ((x) - 50) / 50)
+
+#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg,
state->regs[reg])
+#define ch7006_save_reg(client, state, reg) state->regs[reg] =
ch7006_read(client, reg)
+
+/* Fixed hardware specs */
+
+#define CH7006_FREQ0 14318
+#define CH7006_MAXN 650
+#define CH7006_MAXM 315
+
+/* Register definitions */
+
+#define CH7006_DISPMODE 0x00
+#define CH7006_DISPMODE_INPUT_RES 0, 7:5
+#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
+#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
+#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
+#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
+#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
+#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
+#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
+#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
+#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
+#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
+#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
+#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
+#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
+#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
+#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
+#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
+
+#define CH7006_FFILTER 0x01
+#define CH7006_FFILTER_TEXT 0, 5:4
+#define CH7006_FFILTER_LUMA 0, 3:2
+#define CH7006_FFILTER_CHROMA 0, 1:0
+#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
+
+#define CH7006_BWIDTH 0x03
+#define CH7006_BWIDTH_5L_FFILER (1 << 7)
+#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
+#define CH7006_BWIDTH_CHROMA 0, 5:4
+#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
+#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
+#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
+
+#define CH7006_INPUT_FORMAT 0x04
+#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
+#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
+#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
+
+#define CH7006_CLKMODE 0x06
+#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
+#define CH7006_CLKMODE_MASTER (1 << 6)
+#define CH7006_CLKMODE_POS_EDGE (1 << 4)
+#define CH7006_CLKMODE_XCM 0, 3:2
+#define CH7006_CLKMODE_PCM 0, 1:0
+
+#define CH7006_START_ACTIVE 0x07
+#define CH7006_START_ACTIVE_0 0, 7:0
+
+#define CH7006_POV 0x08
+#define CH7006_POV_START_ACTIVE_8 8, 2:2
+#define CH7006_POV_HPOS_8 8, 1:1
+#define CH7006_POV_VPOS_8 8, 0:0
+
+#define CH7006_BLACK_LEVEL 0x09
+#define CH7006_BLACK_LEVEL_0 0, 7:0
+
+#define CH7006_HPOS 0x0a
+#define CH7006_HPOS_0 0, 7:0
+
+#define CH7006_VPOS 0x0b
+#define CH7006_VPOS_0 0, 7:0
+
+#define CH7006_INPUT_SYNC 0x0d
+#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
+#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
+#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
+#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
+
+#define CH7006_POWER 0x0e
+#define CH7006_POWER_SCART (1 << 4)
+#define CH7006_POWER_RESET (1 << 3)
+#define CH7006_POWER_LEVEL 0, 2:0
+#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
+#define CH7006_POWER_LEVEL_POWER_OFF 0x1
+#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
+#define CH7006_POWER_LEVEL_NORMAL 0x3
+#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
+
+#define CH7006_DETECT 0x10
+#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
+#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
+#define CH7006_DETECT_CVBS_TEST (1 << 1)
+#define CH7006_DETECT_SENSE (1 << 0)
+
+#define CH7006_CONTRAST 0x11
+#define CH7006_CONTRAST_0 0, 2:0
+
+#define CH7006_PLLOV 0x13
+#define CH7006_PLLOV_N_8 8, 2:1
+#define CH7006_PLLOV_M_8 8, 0:0
+
+#define CH7006_PLLM 0x14
+#define CH7006_PLLM_0 0, 7:0
+
+#define CH7006_PLLN 0x15
+#define CH7006_PLLN_0 0, 7:0
+
+#define CH7006_BCLKOUT 0x17
+
+#define CH7006_SUBC_INC0 0x18
+#define CH7006_SUBC_INC0_28 28, 3:0
+
+#define CH7006_SUBC_INC1 0x19
+#define CH7006_SUBC_INC1_24 24, 3:0
+
+#define CH7006_SUBC_INC2 0x1a
+#define CH7006_SUBC_INC2_20 20, 3:0
+
+#define CH7006_SUBC_INC3 0x1b
+#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
+#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
+#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
+#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
+#define CH7006_SUBC_INC3_16 16, 3:0
+
+#define CH7006_SUBC_INC4 0x1c
+#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
+#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
+#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
+#define CH7006_SUBC_INC4_12 12, 3:0
+
+#define CH7006_SUBC_INC5 0x1d
+#define CH7006_SUBC_INC5_8 8, 3:0
+
+#define CH7006_SUBC_INC6 0x1e
+#define CH7006_SUBC_INC6_4 4, 3:0
+
+#define CH7006_SUBC_INC7 0x1f
+#define CH7006_SUBC_INC7_0 0, 3:0
+
+#define CH7006_PLL_CONTROL 0x20
+#define CH7006_PLL_CONTROL_CPI (1 << 5)
+#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
+#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
+#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
+#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
+#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
+
+#define CH7006_CALC_SUBC_INC0 0x21
+#define CH7006_CALC_SUBC_INC0_24 24, 4:3
+#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
+#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
+
+#define CH7006_CALC_SUBC_INC1 0x22
+#define CH7006_CALC_SUBC_INC1_16 16, 7:0
+
+#define CH7006_CALC_SUBC_INC2 0x23
+#define CH7006_CALC_SUBC_INC2_8 8, 7:0
+
+#define CH7006_CALC_SUBC_INC3 0x24
+#define CH7006_CALC_SUBC_INC3_0 0, 7:0
+
+#define CH7006_VERSION_ID 0x25
+
+#endif
diff --git a/include/drm/i2c/ch7006.h b/include/drm/i2c/ch7006.h
new file mode 100644
index 0000000..8390b43
--- /dev/null
+++ b/include/drm/i2c/ch7006.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __DRM_I2C_CH7006_H__
+#define __DRM_I2C_CH7006_H__
+
+/**
+ * struct ch7006_encoder_params
+ *
+ * Describes how the ch7006 is wired up with the GPU. It should be
+ * used as the @params parameter of its @set_config method.
+ *
+ * See "http://www.chrontel.com/pdf/7006.pdf" for their precise
+ * meaning.
+ */
+struct ch7006_encoder_params {
+ enum {
+ CH7006_FORMAT_RGB16 = 0,
+ CH7006_FORMAT_YCrCb24m16,
+ CH7006_FORMAT_RGB24m16,
+ CH7006_FORMAT_RGB15,
+ CH7006_FORMAT_RGB24m12C,
+ CH7006_FORMAT_RGB24m12I,
+ CH7006_FORMAT_RGB24m8,
+ CH7006_FORMAT_RGB16m8,
+ CH7006_FORMAT_RGB15m8,
+ CH7006_FORMAT_YCrCb24m8,
+ } input_format;
+
+ enum {
+ CH7006_CLOCK_SLAVE = 0,
+ CH7006_CLOCK_MASTER,
+ } clock_mode;
+
+ enum {
+ CH7006_CLOCK_EDGE_NEG = 0,
+ CH7006_CLOCK_EDGE_POS,
+ } clock_edge;
+
+ int xcm, pcm;
+
+ enum {
+ CH7006_SYNC_SLAVE = 0,
+ CH7006_SYNC_MASTER,
+ } sync_direction;
+
+ enum {
+ CH7006_SYNC_SEPARATED = 0,
+ CH7006_SYNC_EMBEDDED,
+ } sync_encoding;
+
+ enum {
+ CH7006_POUT_1_8V = 0,
+ CH7006_POUT_3_3V,
+ } pout_level;
+
+ enum {
+ CH7006_ACTIVE_HSYNC = 0,
+ CH7006_ACTIVE_DSTART,
+ } active_detect;
+};
+
+#endif
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 09/10] drm/nouveau: Import <nv17 TV-out support.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/Makefile | 4 +-
drivers/gpu/drm/nouveau/nouveau_bios.c | 33 +++-
drivers/gpu/drm/nouveau/nouveau_drv.h | 4 +
drivers/gpu/drm/nouveau/nouveau_i2c.c | 6 +-
drivers/gpu/drm/nouveau/nouveau_i2c.h | 1 +
drivers/gpu/drm/nouveau/nv04_display.c | 6 +-
drivers/gpu/drm/nouveau/nv04_tv.c | 306 ++++++++++++++++++++++++++++++++
7 files changed, 352 insertions(+), 8 deletions(-)
create mode 100644 drivers/gpu/drm/nouveau/nv04_tv.c
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 4965e00..a79acec 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -18,8 +18,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o
nouveau_mem.o \
nv04_instmem.o nv50_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
- nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
- nv04_dac.o nv04_dfp.o
+ nv04_dac.o nv04_dfp.o nv04_tv.o \
+ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c
b/drivers/gpu/drm/nouveau/nouveau_bios.c
index ec0614f..36f2ed3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -4498,6 +4498,16 @@ static void fabricate_dvi_i_output(struct parsed_dcb
*dcb, bool twoHeads)
#endif
}
+static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
+{
+ struct dcb_entry *entry = new_dcb_entry(dcb);
+
+ entry->type = 1;
+ entry->i2c_index = LEGACY_I2C_TV;
+ entry->heads = twoHeads ? 3 : 1;
+ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
+}
+
static bool
parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
uint32_t conn, uint32_t conf, struct dcb_entry *entry)
@@ -4734,6 +4744,11 @@ static int parse_dcb_table(struct drm_device *dev, struct
nvbios *bios, bool two
"assuming a CRT output exists\n");
/* this situation likely means a really old card, pre DCB */
fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
+
+ if (nv04_tv_identify(dev,
+ bios->legacy.i2c_indices.tv) >= 0)
+ fabricate_tv_output(dcb, twoHeads);
+
return 0;
}
@@ -4794,9 +4809,19 @@ static int parse_dcb_table(struct drm_device *dev, struct
nvbios *bios, bool two
NV_TRACEWARN(dev, "No useful information in BIOS output table; "
"adding all possible outputs\n");
fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
- if (bios->tmds.output0_script_ptr ||
- bios->tmds.output1_script_ptr)
+
+ /* Attempt to detect TV before DVI because the test
+ * for the former is more accurate and it rules the
+ * latter out.
+ */
+ if (nv04_tv_identify(dev,
+ bios->legacy.i2c_indices.tv) >= 0)
+ fabricate_tv_output(dcb, twoHeads);
+
+ else if (bios->tmds.output0_script_ptr ||
+ bios->tmds.output1_script_ptr)
fabricate_dvi_i_output(dcb, twoHeads);
+
return 0;
}
@@ -5031,6 +5056,8 @@ nouveau_bios_init(struct drm_device *dev)
uint32_t saved_nv_pextdev_boot_0;
int ret;
+ dev_priv->vbios = &bios->pub;
+
if (!NVInitVBIOS(dev))
return -ENODEV;
@@ -5066,8 +5093,6 @@ nouveau_bios_init(struct drm_device *dev)
bios_wr32(dev, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
- dev_priv->vbios = &bios->pub;
-
ret = nouveau_run_vbios_init(dev);
if (ret) {
dev_priv->vbios = NULL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index b35df18..aa74ff6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -930,6 +930,10 @@ extern void nv04_dfp_bind_head(struct drm_device *dev,
struct dcb_entry *dcbent,
extern void nv04_dfp_disable(struct drm_device *dev, int head);
extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
+/* nv04_tv.c */
+extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
+extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
+
/* nv04_display.c */
extern int nv04_display_create(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c
b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index c3b9a6f..5ac9e89 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -181,6 +181,7 @@ nouveau_i2c_new(struct drm_device *dev, const char *name,
unsigned index,
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.algo_data = &i2c->algo;
i2c->dev = dev;
+ i2c->index = index;
switch (dcbi2c->port_type) {
case 0:
@@ -235,11 +236,14 @@ void
nouveau_i2c_del(struct nouveau_i2c_chan **pi2c)
{
struct nouveau_i2c_chan *i2c = *pi2c;
+ struct drm_nouveau_private *dev_priv;
if (!i2c)
return;
- *pi2c = NULL;
+ dev_priv = i2c->dev->dev_private;
+
+ dev_priv->vbios->dcb->i2c[i2c->index].chan = *pi2c = NULL;
i2c_del_adapter(&i2c->adapter);
kfree(i2c);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h
b/drivers/gpu/drm/nouveau/nouveau_i2c.h
index e04c77e..babb9f1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -33,6 +33,7 @@ struct nouveau_i2c_chan {
struct drm_device *dev;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo;
+ unsigned index;
unsigned rd;
unsigned wr;
unsigned data;
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c
b/drivers/gpu/drm/nouveau/nv04_display.c
index 4826e13..ceed5bf 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -139,7 +139,11 @@ nv04_display_create(struct drm_device *dev)
ret = nv04_dfp_create(dev, dcbent);
break;
case OUTPUT_TV:
- continue;
+ if (dcbent->location == DCB_LOC_ON_CHIP)
+ continue;
+ else
+ ret = nv04_tv_create(dev, dcbent);
+ break;
default:
NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
continue;
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c
b/drivers/gpu/drm/nouveau/nv04_tv.c
new file mode 100644
index 0000000..92e74ae
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "drm_crtc_helper.h"
+
+#include "i2c/ch7006.h"
+
+static struct {
+ struct i2c_board_info board_info;
+ struct drm_encoder_funcs funcs;
+ struct drm_encoder_helper_funcs hfuncs;
+ void *params;
+
+} nv04_tv_encoder_info[] = {
+ {
+ .board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
+ .params = &(struct ch7006_encoder_params) {
+ CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
+ 0, 0, 0,
+ CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
+ CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
+ },
+ },
+};
+
+static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
+{
+ struct i2c_msg msg = {
+ .addr = addr,
+ .len = 0,
+ };
+
+ return i2c_transfer(adapter, &msg, 1) == 1;
+}
+
+int nv04_tv_identify(struct drm_device *dev, int i2c_index)
+{
+ char adaptername[11];
+ struct nouveau_i2c_chan *i2c;
+ bool was_locked;
+ int i, ret;
+
+ NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
+
+ snprintf(adaptername, 11, "DCB-I2C-%d", i2c_index);
+ if (nouveau_i2c_new(dev, adaptername, i2c_index, &i2c))
+ return -ENODEV;
+
+ was_locked = NVLockVgaCrtcs(dev, false);
+
+ for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
+ if (probe_i2c_addr(&i2c->adapter,
+ nv04_tv_encoder_info[i].board_info.addr)) {
+ ret = i;
+ break;
+ }
+ }
+
+ if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
+ NV_TRACE(dev, "Detected TV encoder: %s\n",
+ nv04_tv_encoder_info[i].board_info.type);
+
+ } else {
+ NV_TRACE(dev, "No TV encoders found.\n");
+
+ nouveau_i2c_del(&i2c);
+ i = -ENODEV;
+ }
+
+ NVLockVgaCrtcs(dev, was_locked);
+ return i;
+}
+
+#define PLLSEL_TV_CRTC1_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
+#define PLLSEL_TV_CRTC2_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
+static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
+ uint8_t crtc1A;
+
+ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
+ mode, nv_encoder->dcb->index);
+
+ state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ int head = nouveau_crtc(encoder->crtc)->index;
+ crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
+
+ state->pllsel |= head? PLLSEL_TV_CRTC2_MASK : PLLSEL_TV_CRTC1_MASK;
+
+ /* Inhibit hsync */
+ crtc1A |= 0x80;
+
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
+ }
+
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
+
+ to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+}
+
+static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
+
+ state->tv_setup = 0;
+
+ if (bind) {
+ state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+ state->CRTC[NV_CIO_CRE_49] |= 0x10;
+ } else {
+ state->CRTC[NV_CIO_CRE_49] &= ~0x10;
+ }
+
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
+ state->CRTC[NV_CIO_CRE_LCD__INDEX]);
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
+ state->CRTC[NV_CIO_CRE_49]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
+ state->tv_setup);
+}
+
+static void nv04_tv_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ nv04_dfp_disable(dev, head);
+
+ if (nv_two_heads(dev))
+ nv04_tv_bind(dev, head ^ 1, false);
+
+ nv04_tv_bind(dev, head, true);
+}
+
+static void nv04_tv_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nv04_crtc_reg *regp =
&dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+
+ regp->tv_htotal = adjusted_mode->htotal;
+ regp->tv_vtotal = adjusted_mode->vtotal;
+
+ /* These delay the TV signals with respect to the VGA port,
+ * they might be useful if we ever allow a CRTC to drive
+ * multiple outputs.
+ */
+ regp->tv_hskew = 1;
+ regp->tv_hsync_delay = 1;
+ regp->tv_hsync_delay2 = 64;
+ regp->tv_vskew = 1;
+ regp->tv_vsync_delay = 1;
+
+ to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode,
adjusted_mode);
+}
+
+static void nv04_tv_commit(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
nv_crtc->index,
+ '@' + ffs(nv_encoder->dcb->or));
+}
+
+static void nv04_tv_destroy(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
+
+ drm_encoder_cleanup(encoder);
+
+ kfree(nv_encoder);
+}
+
+int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct i2c_adapter *adap;
+ struct drm_encoder_funcs *funcs = NULL;
+ struct drm_encoder_helper_funcs *hfuncs = NULL;
+ struct drm_encoder_slave_funcs *sfuncs = NULL;
+ int i2c_index = entry->i2c_index;
+ int type, ret;
+ bool was_locked;
+
+ /* Ensure that we can talk to this encoder */
+ type = nv04_tv_identify(dev, i2c_index);
+ if (type < 0)
+ return type;
+
+ /* Allocate the necessary memory */
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+
+ /* Initialize the common members */
+ encoder = to_drm_encoder(nv_encoder);
+
+ funcs = &nv04_tv_encoder_info[type].funcs;
+ hfuncs = &nv04_tv_encoder_info[type].hfuncs;
+
+ drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
+ drm_encoder_helper_add(encoder, hfuncs);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ /* Run the slave-specific initialization */
+ adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
+
+ was_locked = NVLockVgaCrtcs(dev, false);
+
+ ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
+ &nv04_tv_encoder_info[type].board_info);
+
+ NVLockVgaCrtcs(dev, was_locked);
+
+ if (ret < 0)
+ goto fail;
+
+ /* Fill the function pointers */
+ sfuncs = to_encoder_slave(encoder)->slave_funcs;
+
+ *funcs = (struct drm_encoder_funcs) {
+ .destroy = nv04_tv_destroy,
+ };
+
+ *hfuncs = (struct drm_encoder_helper_funcs) {
+ .dpms = nv04_tv_dpms,
+ .save = sfuncs->save,
+ .restore = sfuncs->restore,
+ .mode_fixup = sfuncs->mode_fixup,
+ .prepare = nv04_tv_prepare,
+ .commit = nv04_tv_commit,
+ .mode_set = nv04_tv_mode_set,
+ .detect = sfuncs->detect,
+ };
+
+ /* Set the slave encoder configuration */
+ sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
+
+ return 0;
+
+fail:
+ drm_encoder_cleanup(encoder);
+
+ kfree(nv_encoder);
+ return ret;
+}
--
1.6.3.3
Francisco Jerez
2009-Aug-13 12:01 UTC
[Nouveau] [PATCHv2 10/10] drm/nouveau: Import >=nv17 TV-out support.
Signed-off-by: Francisco Jerez <currojerez at riseup.net>
---
drivers/gpu/drm/nouveau/Makefile | 2 +-
drivers/gpu/drm/nouveau/nouveau_drv.h | 6 +
drivers/gpu/drm/nouveau/nv04_dac.c | 38 ++-
drivers/gpu/drm/nouveau/nv04_display.c | 2 +-
drivers/gpu/drm/nouveau/nv17_tv.c | 625 +++++++++++++++++++++++++++++++
drivers/gpu/drm/nouveau/nv17_tv.h | 155 ++++++++
drivers/gpu/drm/nouveau/nv17_tv_modes.c | 580 ++++++++++++++++++++++++++++
7 files changed, 1401 insertions(+), 7 deletions(-)
create mode 100644 drivers/gpu/drm/nouveau/nv17_tv.c
create mode 100644 drivers/gpu/drm/nouveau/nv17_tv.h
create mode 100644 drivers/gpu/drm/nouveau/nv17_tv_modes.c
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index a79acec..5a46cdd 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -18,7 +18,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o
nouveau_mem.o \
nv04_instmem.o nv50_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
- nv04_dac.o nv04_dfp.o nv04_tv.o \
+ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index aa74ff6..65751de 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -934,6 +934,12 @@ extern void nv04_dfp_update_fp_control(struct drm_encoder
*encoder, int mode);
extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
+/* nv17_tv.c */
+extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
+extern enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector,
+ uint32_t pin_mask);
+
/* nv04_display.c */
extern int nv04_display_create(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c
b/drivers/gpu/drm/nouveau/nv04_dac.c
index 05fc393..31f7bbf 100644
--- a/drivers/gpu/drm/nouveau/nv04_dac.c
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -220,14 +220,21 @@ enum drm_connector_status nv17_dac_detect(struct
drm_encoder *encoder,
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
- saved_rtest_ctrl, temp, routput;
+ saved_rtest_ctrl, temp, saved_gpio_ext = 0, routput;
int head, present = 0;
#define RGB_TEST_DATA(r,g,b) (r << 0 | g << 10 | b << 20)
- testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+ if (dcb->type == OUTPUT_TV) {
+ testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
- if (dev_priv->vbios->dactestval)
- testval = dev_priv->vbios->dactestval;
+ if (dev_priv->vbios->tvdactestval)
+ testval = dev_priv->vbios->tvdactestval;
+ } else {
+ testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+
+ if (dev_priv->vbios->dactestval)
+ testval = dev_priv->vbios->dactestval;
+ }
saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
@@ -241,6 +248,13 @@ enum drm_connector_status nv17_dac_detect(struct
drm_encoder *encoder,
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
}
+ if (nv_arch(dev) >= NV_30) {
+ saved_gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
+
+ NVWriteCRTC(dev, 0, NV_PCRTC_GPIO_EXT, (saved_gpio_ext & ~(3 <<
20)) |
+ (dcb->type == OUTPUT_TV ? (1 << 20) : 0));
+ }
+
msleep(4);
saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
@@ -254,6 +268,13 @@ enum drm_connector_status nv17_dac_detect(struct
drm_encoder *encoder,
/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
routput = (saved_routput & 0xfffffece) | head << 8;
+ if (nv_arch(dev) >= NV_40) {
+ if (dcb->type == OUTPUT_TV)
+ routput |= 1 << 20;
+ else
+ routput &= ~(1 << 20);
+ }
+
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
msleep(1);
@@ -269,7 +290,11 @@ enum drm_connector_status nv17_dac_detect(struct
drm_encoder *encoder,
temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
- present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
+ if (dcb->type == OUTPUT_TV)
+ present = (nv17_tv_detect(encoder, connector, (temp >> 28) & 0xe)
+ == connector_status_connected);
+ else
+ present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
@@ -283,6 +308,9 @@ enum drm_connector_status nv17_dac_detect(struct drm_encoder
*encoder,
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
+ if (nv_arch(dev) >= NV_30)
+ NVWriteRAMDAC(dev, 0, NV_PCRTC_GPIO_EXT, saved_gpio_ext);
+
if (present) {
NV_INFO(dev, "Load detected on output %c\n", '@' +
ffs(dcb->or));
return connector_status_connected;
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c
b/drivers/gpu/drm/nouveau/nv04_display.c
index ceed5bf..8d6ce2e 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -140,7 +140,7 @@ nv04_display_create(struct drm_device *dev)
break;
case OUTPUT_TV:
if (dcbent->location == DCB_LOC_ON_CHIP)
- continue;
+ ret = nv17_tv_create(dev, dcbent);
else
ret = nv04_tv_create(dev, dcbent);
break;
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c
b/drivers/gpu/drm/nouveau/nv17_tv.c
new file mode 100644
index 0000000..6055043
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nv17_tv.h"
+
+enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector,
+ uint32_t pin_mask)
+{
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+ tv_enc->pin_mask = pin_mask;
+
+ switch (pin_mask) {
+ case 0x2:
+ case 0x4:
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
+ break;
+ case 0xc:
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
+ break;
+ case 0xe:
+ if (nouveau_encoder(encoder)->dcb->tvconf.has_component_output)
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
+ else
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
+ break;
+ default:
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+ break;
+ }
+
+ drm_connector_property_set_value(connector,
+ encoder->dev->mode_config.tv_subconnector_property,
+ tv_enc->subconnector);
+
+ return tv_enc->subconnector? connector_status_connected
+ : connector_status_disconnected;
+}
+
+static int nv17_tv_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ struct drm_display_mode *mode;
+ int n = 0;
+
+ if (tv_norm->kind == CTV_ENC_MODE) {
+ struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode;
+ int i;
+ struct {
+ int hdisplay;
+ int vdisplay;
+ } modes[] = {{ 640, 400 },
+ { 640, 480 },
+ { 720, 480 },
+ { 720, 576 },
+ { 800, 600 },
+ { 1024, 768 },
+ { 1280, 720 },
+ { 1280, 1024 },
+ { 1920, 1080 }};
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ if (modes[i].hdisplay > output_mode->hdisplay ||
+ modes[i].vdisplay > output_mode->vdisplay)
+ continue;
+
+ if (modes[i].hdisplay == output_mode->hdisplay &&
+ modes[i].vdisplay == output_mode->vdisplay) {
+ mode = drm_mode_duplicate(encoder->dev, output_mode);
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ } else {
+ mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
+ modes[i].vdisplay, 60, false,
+ output_mode->flags & DRM_MODE_FLAG_INTERLACE);
+ }
+
+ /* CVT modes are sometimes unsuitable... */
+ if (output_mode->hdisplay <= 720
+ || output_mode->hdisplay >= 1920) {
+ mode->htotal = output_mode->htotal;
+ mode->hsync_start = (mode->hdisplay + (mode->htotal
+ - mode->hdisplay) * 9 / 10) & ~7;
+ mode->hsync_end = mode->hsync_start + 8;
+ }
+ if (output_mode->vdisplay >= 1024) {
+ mode->vtotal = output_mode->vtotal;
+ mode->vsync_start = output_mode->vsync_start;
+ mode->vsync_end = output_mode->vsync_end;
+ }
+
+ mode->type |= DRM_MODE_TYPE_DRIVER;
+ drm_mode_probed_add(connector, mode);
+ n++;
+ }
+
+
+ } else {
+ struct drm_display_mode *tv_mode;
+
+ for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
+ mode = drm_mode_duplicate(encoder->dev, tv_mode);
+
+ mode->clock = tv_norm->tv_enc_mode.vrefresh
+ *mode->htotal/1000*mode->vtotal/1000;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ mode->clock *= 2;
+
+ if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
+ mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+ n++;
+ }
+ }
+
+ return n;
+}
+
+static int nv17_tv_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+ if (tv_norm->kind == CTV_ENC_MODE) {
+ struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode;
+
+ if (mode->clock > 400000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->hdisplay > output_mode->hdisplay ||
+ mode->vdisplay > output_mode->vdisplay)
+ return MODE_BAD;
+
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !+
(output_mode->flags & DRM_MODE_FLAG_INTERLACE))
+ return MODE_NO_INTERLACE;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ } else {
+ const int vsync_tolerance = 10;
+
+ if (mode->clock > 70000)
+ return MODE_CLOCK_HIGH;
+
+ if (abs(drm_mode_vrefresh(mode) - tv_norm->tv_enc_mode.vrefresh) >
+ vsync_tolerance)
+ return MODE_VSYNC;
+
+ /* The encoder takes care of the actual interlacing */
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+ }
+
+ return MODE_OK;
+}
+
+static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+ if (tv_norm->kind == CTV_ENC_MODE)
+ adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
+ else
+ adjusted_mode->clock = 90000;
+
+ return true;
+}
+
+static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+ if (nouveau_encoder(encoder)->last_dpms == mode)
+ return;
+ nouveau_encoder(encoder)->last_dpms = mode;
+
+ NV_TRACE(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
+ mode, nouveau_encoder(encoder)->dcb->index);
+
+ regs->ptv_200 &= ~1;
+
+ if (tv_norm->kind == CTV_ENC_MODE) {
+ nv04_dfp_update_fp_control(encoder, mode);
+
+ } else {
+ nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ regs->ptv_200 |= 1;
+ }
+
+ nv_load_ptv(dev, regs, 200);
+
+ if (nv_arch(dev) >= NV_30) {
+ uint32_t *gpio_ext = &dev_priv->mode_reg.crtc_reg[0].gpio_ext;
+
+ *gpio_ext &= ~(3 << 20);
+ if (mode == DRM_MODE_DPMS_ON)
+ *gpio_ext |= 1 << 20;
+
+ NVWriteCRTC(dev, 0, NV_PCRTC_GPIO_EXT, *gpio_ext);
+ }
+
+ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+static void nv17_tv_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ int head = nouveau_crtc(encoder->crtc)->index;
+ uint8_t *cr_lcd =
&dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_LCD__INDEX];
+ uint32_t dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
+ uint32_t dacclk;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ nv04_dfp_disable(dev, head);
+
+ /* Unbind any FP encoders from this head if we need the FP
+ * stuff enabled. */
+ if (tv_norm->kind == CTV_ENC_MODE) {
+ struct drm_encoder *enc;
+
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
+ struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
+
+ if ((dcb->type == OUTPUT_TMDS || dcb->type == OUTPUT_LVDS)
+ && !enc->crtc && nv04_dfp_get_bound_head(dev, dcb) ==
head)
+ nv04_dfp_bind_head(dev, dcb, head ^ 1, dev_priv->VBIOS.fp.dual_link);
+ }
+
+ }
+
+ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+ * at LCD__INDEX which we don't alter
+ */
+ if (!(*cr_lcd & 0x44)) {
+ if (tv_norm->kind == CTV_ENC_MODE)
+ *cr_lcd = 0x1 | (head ? 0x0 : 0x8);
+ else
+ *cr_lcd = 0;
+ }
+
+ /* Set the DACCLK register */
+ dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
+
+ if (nv_arch(dev) == NV_40)
+ dacclk |= 1 << 20;
+
+ if (tv_norm->kind == CTV_ENC_MODE) {
+ dacclk |= 0x20;
+
+ if (head)
+ dacclk |= 0x100;
+ else
+ dacclk &= ~0x100;
+
+ } else {
+ dacclk |= 0x10;
+
+ }
+
+ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
+}
+
+static void nv17_tv_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
+ struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ int i;
+
+ regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
+ regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
+ regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
+ regs->tv_setup = 1;
+ regs->ramdac_8c0 = 0x0;
+
+ if (tv_norm->kind == TV_ENC_MODE) {
+ tv_regs->ptv_200 = 0x13111100;
+ if (head)
+ tv_regs->ptv_200 |= 0x10;
+
+ tv_regs->ptv_20c = 0x808010;
+ tv_regs->ptv_304 = 0x2d00000;
+ tv_regs->ptv_600 = 0x0;
+ tv_regs->ptv_60c = 0x0;
+ tv_regs->ptv_610 = 0x1e00000;
+
+ if (tv_norm->tv_enc_mode.vdisplay == 576) {
+ tv_regs->ptv_508 = 0x1200000;
+ tv_regs->ptv_614 = 0x33;
+
+ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
+ tv_regs->ptv_508 = 0xf00000;
+ tv_regs->ptv_614 = 0x13;
+ }
+
+ if (nv_arch(dev) >= NV_30) {
+ tv_regs->ptv_500 = 0xe8e0;
+ tv_regs->ptv_504 = 0x1710;
+ tv_regs->ptv_604 = 0x0;
+ tv_regs->ptv_608 = 0x0;
+ } else {
+ if (tv_norm->tv_enc_mode.vdisplay == 576) {
+ tv_regs->ptv_604 = 0x20;
+ tv_regs->ptv_608 = 0x10;
+ tv_regs->ptv_500 = 0x19710;
+ tv_regs->ptv_504 = 0x68f0;
+
+ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
+ tv_regs->ptv_604 = 0x10;
+ tv_regs->ptv_608 = 0x20;
+ tv_regs->ptv_500 = 0x4b90;
+ tv_regs->ptv_504 = 0x1b480;
+ }
+ }
+
+ for (i = 0; i < 0x40; i++)
+ tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
+
+ } else {
+ struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode;
+
+ /* The registers in PRAMDAC+0xc00 control some timings and CSC
+ * parameters for the CTV encoder (It's only used for "HD" TV
+ * modes, I don't think I have enough working to guess what
+ * they exactly mean...), it's probably connected at the
+ * output of the FP encoder, but it also needs the analog
+ * encoder in its OR enabled and routed to the head it's
+ * using. It's enabled with the DACCLK register, bits [5:4].
+ */
+ for (i = 0; i < 38; i++)
+ regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
+
+ regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
+ regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+ regs->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
+ regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+ regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
+ max((output_mode->hdisplay-600)/40 - 1, 1);
+
+ regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
+ regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
+ regs->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
+ regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
+ regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
+
+ regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
+ NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
+ NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
+
+ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
+ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
+
+ regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
+ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
+ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
+ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
+ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
+ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
+ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
+
+ regs->fp_debug_2 = 0;
+
+ regs->fp_margin_color = 0x801080;
+
+ }
+}
+
+static void nv17_tv_commit(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+ if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
+ nv17_tv_update_rescaler(encoder);
+ nv17_tv_update_properties(encoder);
+ } else {
+ nv17_ctv_update_rescaler(encoder);
+ }
+
+ nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
+
+ /* This could use refinement for flatpanels, but it should work this way */
+ if (dev_priv->chipset < 0x44)
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0xf0000000);
+ else
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder), 0x00100000);
+
+ helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+static void nv17_tv_save(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+ nouveau_encoder(encoder)->restore.output + NVReadRAMDAC(dev, 0,
NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder));
+
+ nv17_tv_state_save(dev, &tv_enc->saved_state);
+
+ tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
+}
+
+static void nv17_tv_restore(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
+ nouveau_encoder(encoder)->restore.output);
+
+ nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
+}
+
+static int nv17_tv_create_resources(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *conf = &dev->mode_config;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+
+ drm_mode_create_tv_properties(dev,
+ dcb->tvconf.has_component_output? NUM_TV_NORMS
+ : NUM_LD_TV_NORMS, nv17_tv_norm_names);
+
+ drm_connector_attach_property(connector,
conf->tv_select_subconnector_property,
+ tv_enc->select_subconnector);
+ drm_connector_attach_property(connector, conf->tv_subconnector_property,
+ tv_enc->subconnector);
+ drm_connector_attach_property(connector, conf->tv_mode_property,
+ tv_enc->tv_norm);
+ drm_connector_attach_property(connector,
conf->tv_flicker_reduction_property,
+ tv_enc->flicker);
+ drm_connector_attach_property(connector, conf->tv_saturation_property,
+ tv_enc->saturation);
+ drm_connector_attach_property(connector, conf->tv_hue_property,
+ tv_enc->hue);
+ drm_connector_attach_property(connector, conf->tv_overscan_property,
+ tv_enc->overscan);
+
+ return 0;
+}
+
+static int nv17_tv_set_property(struct drm_encoder *encoder,
+ struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_mode_config *conf = &encoder->dev->mode_config;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+ if (property == conf->tv_overscan_property) {
+ tv_enc->overscan = val;
+ if (encoder->crtc) {
+ if (tv_norm->kind == CTV_ENC_MODE)
+ nv17_ctv_update_rescaler(encoder);
+ else
+ nv17_tv_update_rescaler(encoder);
+ }
+
+ } else if (property == conf->tv_saturation_property) {
+ if (tv_norm->kind != TV_ENC_MODE)
+ return -EINVAL;
+
+ tv_enc->saturation = val;
+ nv17_tv_update_properties(encoder);
+
+ } else if (property == conf->tv_hue_property) {
+ if (tv_norm->kind != TV_ENC_MODE)
+ return -EINVAL;
+
+ tv_enc->hue = val;
+ nv17_tv_update_properties(encoder);
+
+ } else if (property == conf->tv_flicker_reduction_property) {
+ if (tv_norm->kind != TV_ENC_MODE)
+ return -EINVAL;
+
+ tv_enc->flicker = val;
+ if (encoder->crtc)
+ nv17_tv_update_rescaler(encoder);
+
+ } else if (property == conf->tv_mode_property) {
+ tv_enc->tv_norm = val;
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+ } else if (property == conf->tv_select_subconnector_property) {
+ if (tv_norm->kind != TV_ENC_MODE)
+ return -EINVAL;
+
+ tv_enc->select_subconnector = val;
+ nv17_tv_update_properties(encoder);
+
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void nv17_tv_destroy(struct drm_encoder *encoder)
+{
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+ NV_DEBUG(encoder->dev, "\n");
+
+ drm_encoder_cleanup(encoder);
+ kfree(tv_enc);
+}
+
+static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
+ .dpms = nv17_tv_dpms,
+ .save = nv17_tv_save,
+ .restore = nv17_tv_restore,
+ .mode_fixup = nv17_tv_mode_fixup,
+ .prepare = nv17_tv_prepare,
+ .commit = nv17_tv_commit,
+ .mode_set = nv17_tv_mode_set,
+ .detect = nv17_dac_detect,
+};
+
+static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
+ .get_modes = nv17_tv_get_modes,
+ .mode_valid = nv17_tv_mode_valid,
+ .create_resources = nv17_tv_create_resources,
+ .set_property = nv17_tv_set_property,
+};
+
+static struct drm_encoder_funcs nv17_tv_funcs = {
+ .destroy = nv17_tv_destroy,
+};
+
+int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+ struct drm_encoder *encoder;
+ struct nv17_tv_encoder *tv_enc = NULL;
+
+ tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
+ if (!tv_enc)
+ return -ENOMEM;
+
+ tv_enc->overscan = 50;
+ tv_enc->flicker = 50;
+ tv_enc->saturation = 50;
+ tv_enc->hue = 0;
+ tv_enc->tv_norm = TV_NORM_PAL;
+ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+ tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
+ tv_enc->pin_mask = 0;
+
+ encoder = to_drm_encoder(&tv_enc->base);
+
+ tv_enc->base.dcb = entry;
+ tv_enc->base.or = ffs(entry->or) - 1;
+
+ drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
+ drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
+ to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h
b/drivers/gpu/drm/nouveau/nv17_tv.h
new file mode 100644
index 0000000..dc17584
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NV17_TV_H__
+#define __NV17_TV_H__
+
+struct nv17_tv_state {
+ uint8_t tv_enc[0x40];
+
+ uint32_t hfilter[4][7];
+ uint32_t hfilter2[4][7];
+ uint32_t vfilter[4][7];
+
+ uint32_t ptv_200;
+ uint32_t ptv_204;
+ uint32_t ptv_208;
+ uint32_t ptv_20c;
+ uint32_t ptv_304;
+ uint32_t ptv_500;
+ uint32_t ptv_504;
+ uint32_t ptv_508;
+ uint32_t ptv_600;
+ uint32_t ptv_604;
+ uint32_t ptv_608;
+ uint32_t ptv_60c;
+ uint32_t ptv_610;
+ uint32_t ptv_614;
+};
+
+enum nv17_tv_norm{
+ TV_NORM_PAL,
+ TV_NORM_PAL_M,
+ TV_NORM_PAL_N,
+ TV_NORM_PAL_NC,
+ TV_NORM_NTSC_M,
+ TV_NORM_NTSC_J,
+ NUM_LD_TV_NORMS,
+ TV_NORM_HD480I = NUM_LD_TV_NORMS,
+ TV_NORM_HD480P,
+ TV_NORM_HD576I,
+ TV_NORM_HD576P,
+ TV_NORM_HD720P,
+ TV_NORM_HD1080I,
+ NUM_TV_NORMS
+};
+
+struct nv17_tv_encoder {
+ struct nouveau_encoder base;
+
+ struct nv17_tv_state state;
+ struct nv17_tv_state saved_state;
+
+ int overscan;
+ int flicker;
+ int saturation;
+ int hue;
+ enum nv17_tv_norm tv_norm;
+ int subconnector;
+ int select_subconnector;
+ uint32_t pin_mask;
+};
+#define to_tv_enc(x) container_of(nouveau_encoder(x), \
+ struct nv17_tv_encoder, base)
+
+extern char *nv17_tv_norm_names[NUM_TV_NORMS];
+
+extern struct nv17_tv_norm_params {
+ enum {
+ TV_ENC_MODE,
+ CTV_ENC_MODE,
+ } kind;
+
+ union {
+ struct {
+ int hdisplay;
+ int vdisplay;
+ int vrefresh; /* mHz */
+
+ uint8_t tv_enc[0x40];
+ } tv_enc_mode;
+
+ struct {
+ struct drm_display_mode mode;
+
+ uint32_t ctv_regs[38];
+ } ctv_enc_mode;
+ };
+
+} nv17_tv_norms[NUM_TV_NORMS];
+#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm])
+
+extern struct drm_display_mode nv17_tv_modes[];
+
+#define interpolate(y0, y1, y2, x) ((y1) + \
+ ((x) < 50 ? (y1) - (y0) : (y2) - (y1)) \
+ * ((x) - 50) / 50)
+
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state);
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state);
+void nv17_tv_update_properties(struct drm_encoder *encoder);
+void nv17_tv_update_rescaler(struct drm_encoder *encoder);
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
+
+/* TV hardware access functions */
+
+static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t
val)
+{
+ nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
+{
+ return nv_rd32(dev, reg);
+}
+
+static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t
val)
+{
+ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
+ nv_write_ptv(dev, NV_PTV_TV_DATA, val);
+}
+
+static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
+{
+ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
+ return nv_read_ptv(dev, NV_PTV_TV_DATA);
+}
+
+#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg,
state->ptv_##reg)
+#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev,
NV_PTV_OFFSET + 0x##reg)
+#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg,
state->tv_enc[0x##reg])
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c
b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
new file mode 100644
index 0000000..569921b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction,
including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nv17_tv.h"
+
+char *nv17_tv_norm_names[NUM_TV_NORMS] = {
+ [TV_NORM_PAL] = "PAL",
+ [TV_NORM_PAL_M] = "PAL-M",
+ [TV_NORM_PAL_N] = "PAL-N",
+ [TV_NORM_PAL_NC] = "PAL-Nc",
+ [TV_NORM_NTSC_M] = "NTSC-M",
+ [TV_NORM_NTSC_J] = "NTSC-J",
+ [TV_NORM_HD480I] = "hd480i",
+ [TV_NORM_HD480P] = "hd480p",
+ [TV_NORM_HD576I] = "hd576i",
+ [TV_NORM_HD576P] = "hd576p",
+ [TV_NORM_HD720P] = "hd720p",
+ [TV_NORM_HD1080I] = "hd1080i"
+};
+
+/* TV standard specific parameters */
+
+struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
+ [TV_NORM_PAL] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 576, 50000, {
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_PAL_M] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 480, 59940, {
+ 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_PAL_N] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 576, 50000, {
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+ 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_PAL_NC] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 576, 50000, {
+ 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_NTSC_M] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 480, 59940, {
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_NTSC_J] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 480, 59940, {
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_HD480I] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 480, 59940, {
+ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
+ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
+ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_HD576I] = { TV_ENC_MODE, {
+ .tv_enc_mode = {720, 576, 50000, {
+ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+ }}}},
+
+
+ [TV_NORM_HD480P] = { CTV_ENC_MODE, {
+ .ctv_enc_mode = {
+ .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25312,
+ 720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
+ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
+ 0x10160004, 0x10060005, 0x1006000c, 0x10060020,
+ 0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
+ 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x70,
+ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
+ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
+ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
+ }}}},
+
+ [TV_NORM_HD576P] = { CTV_ENC_MODE, {
+ .ctv_enc_mode = {
+ .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 25312,
+ 720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
+ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
+ 0x10060001, 0x10060009, 0x10060026, 0x10060027,
+ 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
+ 0x10000fff, 0x10000fff, 0x10000fff, 0x69,
+ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
+ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
+ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
+ }}}},
+
+ [TV_NORM_HD720P] = { CTV_ENC_MODE, {
+ .ctv_enc_mode = {
+ .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 70875,
+ 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
+ 0x66b0021, 0x6004a, 0x1210626, 0x8170000,
+ 0x70004, 0x70016, 0x70017, 0x40f0018,
+ 0x702e8, 0x81702ed, 0xfff, 0xfff,
+ 0xfff, 0xfff, 0xfff, 0xfff,
+ 0xfff, 0xfff, 0xfff, 0x0,
+ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
+ 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
+ 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
+ }}}},
+
+ [TV_NORM_HD1080I] = { CTV_ENC_MODE, {
+ .ctv_enc_mode = {
+ .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 70875,
+ 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
+ | DRM_MODE_FLAG_INTERLACE) },
+ .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
+ 0x8940028, 0x60054, 0xe80870, 0xbf70000,
+ 0xbc70004, 0x70005, 0x70012, 0x70013,
+ 0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
+ 0x1c70237, 0x70238, 0x70244, 0x70245,
+ 0x40f0246, 0x70462, 0x1f70464, 0x0,
+ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
+ 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
+ 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
+ }}}}
+};
+
+/*
+ * The following is some guesswork on how the TV encoder flicker
+ * filter/rescaler works:
+ *
+ * It seems to use some sort of resampling filter, it is controlled
+ * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
+ * control the horizontal and vertical stage respectively, there is
+ * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
+ * but they seem to do nothing. A rough guess might be that they could
+ * be used to independently control the filtering of each interlaced
+ * field, but I don't know how they are enabled. The whole filtering
+ * process seems to be disabled with bits 26:27 of PTV_200, but we
+ * aren't doing that.
+ *
+ * The layout of both register sets is the same:
+ *
+ * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
+ * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
+ *
+ * Each coefficient is stored in bits [31],[15:9] in two's complement
+ * format. They seem to be some kind of weights used in a low-pass
+ * filter. Both A and B coefficients are applied to the 14 nearest
+ * samples on each side (Listed from nearest to furthermost. They
+ * roughly cover 2 framebuffer pixels on each side). They are
+ * probably multiplied with some more hardwired weights before being
+ * used: B-coefficients are applied the same on both sides,
+ * A-coefficients are inverted before being applied to the opposite
+ * side.
+ *
+ * After all the hassle, I got the following formula by empirical
+ * means...
+ */
+
+#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
+
+#define id1 (1LL << 8)
+#define id2 (1LL << 16)
+#define id3 (1LL << 24)
+#define id4 (1LL << 32)
+#define id5 (1LL << 48)
+
+static struct filter_params{
+ int64_t k1;
+ int64_t ki;
+ int64_t ki2;
+ int64_t ki3;
+ int64_t kr;
+ int64_t kir;
+ int64_t ki2r;
+ int64_t ki3r;
+ int64_t kf;
+ int64_t kif;
+ int64_t ki2f;
+ int64_t ki3f;
+ int64_t krf;
+ int64_t kirf;
+ int64_t ki2rf;
+ int64_t ki3rf;
+} fparams[2][4] = {
+ /* Horizontal filter parameters */
+ {
+ {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
+ 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
+ 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
+ -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
+ {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
+ 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
+ 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
+ -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
+ {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
+ 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
+ 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
+ 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
+ {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
+ -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
+ -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
+ 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
+ },
+
+ /* Vertical filter parameters */
+ {
+ {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
+ -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
+ -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
+ 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
+ {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
+ 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
+ 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
+ -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
+ {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
+ 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
+ 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
+ -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
+ {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
+ 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
+ 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
+ -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
+ }
+};
+
+static void tv_setup_filter(struct drm_encoder *encoder)
+{
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ struct drm_display_mode *mode = &encoder->crtc->mode;
+ uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
+ &tv_enc->state.vfilter};
+ int i, j, k;
+
+ int64_t overscan = calc_overscan(tv_enc->overscan);
+ int64_t flicker = (tv_enc->flicker - 50) * id3 / 100;
+ int64_t rs[] = {max(id2, mode->hdisplay * id3 / overscan
+ / tv_norm->tv_enc_mode.hdisplay),
+ max(id2, mode->vdisplay * id3 / overscan
+ / tv_norm->tv_enc_mode.vdisplay)};
+
+ for (k = 0; k < 2; k++) {
+ for (j = 0; j < 4; j++) {
+ struct filter_params *p = &fparams[k][j];
+
+ for (i = 0; i < 7; i++) {
+ int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
+ + (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
+ + (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
+ + (p->krf + p->kirf*i + p->ki2rf*i*i +
p->ki3rf*i*i*i)*flicker*rs[k];
+
+ (*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f
<< 9);
+ }
+ }
+ }
+}
+
+/* Hardware state saving/restoring */
+
+static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t
regs[4][7])
+{
+ int i, j;
+ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 7; j++)
+ regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
+ }
+}
+
+static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t
regs[4][7])
+{
+ int i, j;
+ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 7; j++)
+ nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
+ }
+}
+
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
+{
+ int i;
+
+ for (i = 0; i < 0x40; i++)
+ state->tv_enc[i] = nv_read_tv_enc(dev, i);
+
+ tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
+ tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
+ tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
+
+ nv_save_ptv(dev, state, 200);
+ nv_save_ptv(dev, state, 204);
+ nv_save_ptv(dev, state, 208);
+ nv_save_ptv(dev, state, 20c);
+ nv_save_ptv(dev, state, 304);
+ nv_save_ptv(dev, state, 500);
+ nv_save_ptv(dev, state, 504);
+ nv_save_ptv(dev, state, 508);
+ nv_save_ptv(dev, state, 600);
+ nv_save_ptv(dev, state, 604);
+ nv_save_ptv(dev, state, 608);
+ nv_save_ptv(dev, state, 60c);
+ nv_save_ptv(dev, state, 610);
+ nv_save_ptv(dev, state, 614);
+}
+
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
+{
+ int i;
+
+ for (i = 0; i < 0x40; i++)
+ nv_write_tv_enc(dev, i, state->tv_enc[i]);
+
+ tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
+ tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
+ tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
+
+ nv_load_ptv(dev, state, 200);
+ nv_load_ptv(dev, state, 204);
+ nv_load_ptv(dev, state, 208);
+ nv_load_ptv(dev, state, 20c);
+ nv_load_ptv(dev, state, 304);
+ nv_load_ptv(dev, state, 500);
+ nv_load_ptv(dev, state, 504);
+ nv_load_ptv(dev, state, 508);
+ nv_load_ptv(dev, state, 600);
+ nv_load_ptv(dev, state, 604);
+ nv_load_ptv(dev, state, 608);
+ nv_load_ptv(dev, state, 60c);
+ nv_load_ptv(dev, state, 610);
+ nv_load_ptv(dev, state, 614);
+
+ /* This is required for some settings to kick in. */
+ nv_write_tv_enc(dev, 0x3e, 1);
+ nv_write_tv_enc(dev, 0x3e, 0);
+}
+
+/* Timings similar to the ones the blob sets */
+
+struct drm_display_mode nv17_tv_modes[] = {
+ { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
+ 320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+ { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
+ 320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+ { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
+ 400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
+ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
+ 640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
+ 720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
+ 720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
+ 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
+ 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ {}
+};
+
+void nv17_tv_update_properties(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct nv17_tv_state *regs = &tv_enc->state;
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+ int subconnector = tv_enc->select_subconnector?
tv_enc->select_subconnector
+ : tv_enc->subconnector;
+
+ switch (subconnector) {
+ case DRM_MODE_SUBCONNECTOR_Composite:
+ {
+ regs->ptv_204 = 0x2;
+
+ /* The composite connector may be found on either pin. */
+ if (tv_enc->pin_mask & 0x4)
+ regs->ptv_204 |= 0x010000;
+ else if (tv_enc->pin_mask & 0x2)
+ regs->ptv_204 |= 0x100000;
+ else
+ regs->ptv_204 |= 0x110000;
+
+ regs->tv_enc[0x7] = 0x10;
+ break;
+ }
+ case DRM_MODE_SUBCONNECTOR_SVIDEO:
+ regs->ptv_204 = 0x11012;
+ regs->tv_enc[0x7] = 0x18;
+ break;
+
+ case DRM_MODE_SUBCONNECTOR_Component:
+ regs->ptv_204 = 0x111333;
+ regs->tv_enc[0x7] = 0x14;
+ break;
+
+ case DRM_MODE_SUBCONNECTOR_SCART:
+ regs->ptv_204 = 0x111012;
+ regs->tv_enc[0x7] = 0x18;
+ break;
+ }
+
+ regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20],
255,
+ tv_enc->saturation);
+ regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22],
255,
+ tv_enc->saturation);
+ regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
+
+ nv_load_ptv(dev, regs, 204);
+ nv_load_tv_enc(dev, regs, 7);
+ nv_load_tv_enc(dev, regs, 20);
+ nv_load_tv_enc(dev, regs, 22);
+ nv_load_tv_enc(dev, regs, 25);
+}
+
+void nv17_tv_update_rescaler(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct nv17_tv_state *regs = &tv_enc->state;
+
+ regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
+
+ tv_setup_filter(encoder);
+
+ nv_load_ptv(dev, regs, 208);
+ tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
+ tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
+ tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
+}
+
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
+ struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
+ struct drm_display_mode *output_mode =
&get_tv_norm(encoder)->ctv_enc_mode.mode;
+ int overscan, hmargin, vmargin, hratio, vratio;
+
+ /* The rescaler doesn't do the right thing for interlaced modes. */
+ if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
+ overscan = 100;
+ else
+ overscan = tv_enc->overscan;
+
+ hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
+ vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
+
+ hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
+ overscan);
+ vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
+ overscan);
+
+ hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay -
2*hmargin);
+ vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay -
2*vmargin) & ~3;
+
+ regs->fp_horiz_regs[FP_VALID_START] = hmargin;
+ regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
+ regs->fp_vert_regs[FP_VALID_START] = vmargin;
+ regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
+
+ regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
+ XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
+ NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
+ XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
+
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
+ regs->fp_horiz_regs[FP_VALID_START]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
+ regs->fp_horiz_regs[FP_VALID_END]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
+ regs->fp_vert_regs[FP_VALID_START]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
+ regs->fp_vert_regs[FP_VALID_END]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
+}
--
1.6.3.3
Apparently Analagous Threads
- [PATCH 00/12] TV-out modesetting kernel patches.
- [PATCH] drm/nouveau: use drm debug levels
- [PATCH] drm/nv04: fix null pointer dereferences of native_mode
- [PATCH 1/6] drm/i2c/ch7006: Fix some sparse warnings.
- [PATCHv2 1/6] drm/i2c/ch7006: Fix some sparse warnings.