diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2013-02-10 18:52:58 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2013-02-20 01:01:05 -0500 |
commit | eb6313add6dddf07ea3e50c4caa33a9c3b2379f1 (patch) | |
tree | db60f5990aa58a2c1279f19057e67b9d3867c6d0 /drivers/gpu/drm | |
parent | 476e84e126171d809f9c0b5d97137f5055f95ca8 (diff) |
drm/nv50: initial kms support for off-chip TMDS/DP encoders
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 215 |
2 files changed, 202 insertions, 18 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index b64065781a4a..50a6dd02f7c5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c | |||
@@ -1469,6 +1469,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, | |||
1469 | } | 1469 | } |
1470 | case DCB_OUTPUT_DP: | 1470 | case DCB_OUTPUT_DP: |
1471 | entry->dpconf.sor.link = (conf & 0x00000030) >> 4; | 1471 | entry->dpconf.sor.link = (conf & 0x00000030) >> 4; |
1472 | entry->extdev = (conf & 0x0000ff00) >> 8; | ||
1472 | switch ((conf & 0x00e00000) >> 21) { | 1473 | switch ((conf & 0x00e00000) >> 21) { |
1473 | case 0: | 1474 | case 0: |
1474 | entry->dpconf.link_bw = 162000; | 1475 | entry->dpconf.link_bw = 162000; |
@@ -1490,8 +1491,10 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, | |||
1490 | } | 1491 | } |
1491 | break; | 1492 | break; |
1492 | case DCB_OUTPUT_TMDS: | 1493 | case DCB_OUTPUT_TMDS: |
1493 | if (dcb->version >= 0x40) | 1494 | if (dcb->version >= 0x40) { |
1494 | entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; | 1495 | entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; |
1496 | entry->extdev = (conf & 0x0000ff00) >> 8; | ||
1497 | } | ||
1495 | else if (dcb->version >= 0x30) | 1498 | else if (dcb->version >= 0x30) |
1496 | entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; | 1499 | entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; |
1497 | else if (dcb->version >= 0x22) | 1500 | else if (dcb->version >= 0x22) |
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 5c56575729be..49f26cd86013 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
@@ -1923,6 +1923,184 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) | |||
1923 | } | 1923 | } |
1924 | 1924 | ||
1925 | /****************************************************************************** | 1925 | /****************************************************************************** |
1926 | * PIOR | ||
1927 | *****************************************************************************/ | ||
1928 | |||
1929 | static void | ||
1930 | nv50_pior_dpms(struct drm_encoder *encoder, int mode) | ||
1931 | { | ||
1932 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
1933 | struct nv50_disp *disp = nv50_disp(encoder->dev); | ||
1934 | u32 mthd = (nv_encoder->dcb->type << 12) | nv_encoder->or; | ||
1935 | u32 ctrl = (mode == DRM_MODE_DPMS_ON); | ||
1936 | nv_call(disp->core, NV50_DISP_PIOR_PWR + mthd, ctrl); | ||
1937 | } | ||
1938 | |||
1939 | static bool | ||
1940 | nv50_pior_mode_fixup(struct drm_encoder *encoder, | ||
1941 | const struct drm_display_mode *mode, | ||
1942 | struct drm_display_mode *adjusted_mode) | ||
1943 | { | ||
1944 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
1945 | struct nouveau_connector *nv_connector; | ||
1946 | |||
1947 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | ||
1948 | if (nv_connector && nv_connector->native_mode) { | ||
1949 | if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { | ||
1950 | int id = adjusted_mode->base.id; | ||
1951 | *adjusted_mode = *nv_connector->native_mode; | ||
1952 | adjusted_mode->base.id = id; | ||
1953 | } | ||
1954 | } | ||
1955 | |||
1956 | adjusted_mode->clock *= 2; | ||
1957 | return true; | ||
1958 | } | ||
1959 | |||
1960 | static void | ||
1961 | nv50_pior_commit(struct drm_encoder *encoder) | ||
1962 | { | ||
1963 | } | ||
1964 | |||
1965 | static void | ||
1966 | nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
1967 | struct drm_display_mode *adjusted_mode) | ||
1968 | { | ||
1969 | struct nv50_mast *mast = nv50_mast(encoder->dev); | ||
1970 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
1971 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
1972 | struct nouveau_connector *nv_connector; | ||
1973 | u8 owner = 1 << nv_crtc->index; | ||
1974 | u8 proto, depth; | ||
1975 | u32 *push; | ||
1976 | |||
1977 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | ||
1978 | switch (nv_connector->base.display_info.bpc) { | ||
1979 | case 10: depth = 0x6; break; | ||
1980 | case 8: depth = 0x5; break; | ||
1981 | case 6: depth = 0x2; break; | ||
1982 | default: depth = 0x0; break; | ||
1983 | } | ||
1984 | |||
1985 | switch (nv_encoder->dcb->type) { | ||
1986 | case DCB_OUTPUT_TMDS: | ||
1987 | case DCB_OUTPUT_DP: | ||
1988 | proto = 0x0; | ||
1989 | break; | ||
1990 | default: | ||
1991 | BUG_ON(1); | ||
1992 | break; | ||
1993 | } | ||
1994 | |||
1995 | nv50_pior_dpms(encoder, DRM_MODE_DPMS_ON); | ||
1996 | |||
1997 | push = evo_wait(mast, 8); | ||
1998 | if (push) { | ||
1999 | if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { | ||
2000 | u32 ctrl = (depth << 16) | (proto << 8) | owner; | ||
2001 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
2002 | ctrl |= 0x00001000; | ||
2003 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
2004 | ctrl |= 0x00002000; | ||
2005 | evo_mthd(push, 0x0700 + (nv_encoder->or * 0x040), 1); | ||
2006 | evo_data(push, ctrl); | ||
2007 | } | ||
2008 | |||
2009 | evo_kick(push, mast); | ||
2010 | } | ||
2011 | |||
2012 | nv_encoder->crtc = encoder->crtc; | ||
2013 | } | ||
2014 | |||
2015 | static void | ||
2016 | nv50_pior_disconnect(struct drm_encoder *encoder) | ||
2017 | { | ||
2018 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
2019 | struct nv50_mast *mast = nv50_mast(encoder->dev); | ||
2020 | const int or = nv_encoder->or; | ||
2021 | u32 *push; | ||
2022 | |||
2023 | if (nv_encoder->crtc) { | ||
2024 | nv50_crtc_prepare(nv_encoder->crtc); | ||
2025 | |||
2026 | push = evo_wait(mast, 4); | ||
2027 | if (push) { | ||
2028 | if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { | ||
2029 | evo_mthd(push, 0x0700 + (or * 0x040), 1); | ||
2030 | evo_data(push, 0x00000000); | ||
2031 | } | ||
2032 | |||
2033 | evo_mthd(push, 0x0080, 1); | ||
2034 | evo_data(push, 0x00000000); | ||
2035 | evo_kick(push, mast); | ||
2036 | } | ||
2037 | } | ||
2038 | |||
2039 | nv_encoder->crtc = NULL; | ||
2040 | } | ||
2041 | |||
2042 | static void | ||
2043 | nv50_pior_destroy(struct drm_encoder *encoder) | ||
2044 | { | ||
2045 | drm_encoder_cleanup(encoder); | ||
2046 | kfree(encoder); | ||
2047 | } | ||
2048 | |||
2049 | static const struct drm_encoder_helper_funcs nv50_pior_hfunc = { | ||
2050 | .dpms = nv50_pior_dpms, | ||
2051 | .mode_fixup = nv50_pior_mode_fixup, | ||
2052 | .prepare = nv50_pior_disconnect, | ||
2053 | .commit = nv50_pior_commit, | ||
2054 | .mode_set = nv50_pior_mode_set, | ||
2055 | .disable = nv50_pior_disconnect, | ||
2056 | .get_crtc = nv50_display_crtc_get, | ||
2057 | }; | ||
2058 | |||
2059 | static const struct drm_encoder_funcs nv50_pior_func = { | ||
2060 | .destroy = nv50_pior_destroy, | ||
2061 | }; | ||
2062 | |||
2063 | static int | ||
2064 | nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) | ||
2065 | { | ||
2066 | struct nouveau_drm *drm = nouveau_drm(connector->dev); | ||
2067 | struct nouveau_i2c *i2c = nouveau_i2c(drm->device); | ||
2068 | struct nouveau_i2c_port *ddc = NULL; | ||
2069 | struct nouveau_encoder *nv_encoder; | ||
2070 | struct drm_encoder *encoder; | ||
2071 | int type; | ||
2072 | |||
2073 | switch (dcbe->type) { | ||
2074 | case DCB_OUTPUT_TMDS: | ||
2075 | ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(dcbe->extdev)); | ||
2076 | type = DRM_MODE_ENCODER_TMDS; | ||
2077 | break; | ||
2078 | case DCB_OUTPUT_DP: | ||
2079 | ddc = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(dcbe->extdev)); | ||
2080 | type = DRM_MODE_ENCODER_TMDS; | ||
2081 | break; | ||
2082 | default: | ||
2083 | return -ENODEV; | ||
2084 | } | ||
2085 | |||
2086 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
2087 | if (!nv_encoder) | ||
2088 | return -ENOMEM; | ||
2089 | nv_encoder->dcb = dcbe; | ||
2090 | nv_encoder->or = ffs(dcbe->or) - 1; | ||
2091 | nv_encoder->i2c = ddc; | ||
2092 | |||
2093 | encoder = to_drm_encoder(nv_encoder); | ||
2094 | encoder->possible_crtcs = dcbe->heads; | ||
2095 | encoder->possible_clones = 0; | ||
2096 | drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type); | ||
2097 | drm_encoder_helper_add(encoder, &nv50_pior_hfunc); | ||
2098 | |||
2099 | drm_mode_connector_attach_encoder(connector, encoder); | ||
2100 | return 0; | ||
2101 | } | ||
2102 | |||
2103 | /****************************************************************************** | ||
1926 | * Init | 2104 | * Init |
1927 | *****************************************************************************/ | 2105 | *****************************************************************************/ |
1928 | void | 2106 | void |
@@ -2044,25 +2222,28 @@ nv50_display_create(struct drm_device *dev) | |||
2044 | if (IS_ERR(connector)) | 2222 | if (IS_ERR(connector)) |
2045 | continue; | 2223 | continue; |
2046 | 2224 | ||
2047 | if (dcbe->location != DCB_LOC_ON_CHIP) { | 2225 | if (dcbe->location == DCB_LOC_ON_CHIP) { |
2048 | NV_WARN(drm, "skipping off-chip encoder %d/%d\n", | 2226 | switch (dcbe->type) { |
2049 | dcbe->type, ffs(dcbe->or) - 1); | 2227 | case DCB_OUTPUT_TMDS: |
2050 | continue; | 2228 | case DCB_OUTPUT_LVDS: |
2229 | case DCB_OUTPUT_DP: | ||
2230 | ret = nv50_sor_create(connector, dcbe); | ||
2231 | break; | ||
2232 | case DCB_OUTPUT_ANALOG: | ||
2233 | ret = nv50_dac_create(connector, dcbe); | ||
2234 | break; | ||
2235 | default: | ||
2236 | ret = -ENODEV; | ||
2237 | break; | ||
2238 | } | ||
2239 | } else { | ||
2240 | ret = nv50_pior_create(connector, dcbe); | ||
2051 | } | 2241 | } |
2052 | 2242 | ||
2053 | switch (dcbe->type) { | 2243 | if (ret) { |
2054 | case DCB_OUTPUT_TMDS: | 2244 | NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", |
2055 | case DCB_OUTPUT_LVDS: | 2245 | dcbe->location, dcbe->type, |
2056 | case DCB_OUTPUT_DP: | 2246 | ffs(dcbe->or) - 1, ret); |
2057 | nv50_sor_create(connector, dcbe); | ||
2058 | break; | ||
2059 | case DCB_OUTPUT_ANALOG: | ||
2060 | nv50_dac_create(connector, dcbe); | ||
2061 | break; | ||
2062 | default: | ||
2063 | NV_WARN(drm, "skipping unsupported encoder %d/%d\n", | ||
2064 | dcbe->type, ffs(dcbe->or) - 1); | ||
2065 | continue; | ||
2066 | } | 2247 | } |
2067 | } | 2248 | } |
2068 | 2249 | ||