drm/radeon/kms: add initial Evergreen support (Radeon HD 5xxx)
[deliverable/linux.git] / drivers / gpu / drm / radeon / radeon_display.c
index c85df4afcb7ac09e2f89ec21f116a7ee4bdfaa39..257827806aee0d522c337ac822a12b702cbec8cd 100644 (file)
@@ -68,6 +68,36 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc)
        WREG32(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id);
 }
 
+static void evergreen_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       int i;
+
+       DRM_DEBUG("%d\n", radeon_crtc->crtc_id);
+       WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff);
+
+       WREG32(EVERGREEN_DC_LUT_RW_MODE, radeon_crtc->crtc_id);
+       WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK, 0x00000007);
+
+       WREG32(EVERGREEN_DC_LUT_RW_INDEX, 0);
+       for (i = 0; i < 256; i++) {
+               WREG32(EVERGREEN_DC_LUT_30_COLOR,
+                      (radeon_crtc->lut_r[i] << 20) |
+                      (radeon_crtc->lut_g[i] << 10) |
+                      (radeon_crtc->lut_b[i] << 0));
+       }
+}
+
 static void legacy_crtc_load_lut(struct drm_crtc *crtc)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -100,7 +130,9 @@ void radeon_crtc_load_lut(struct drm_crtc *crtc)
        if (!crtc->enabled)
                return;
 
-       if (ASIC_IS_AVIVO(rdev))
+       if (ASIC_IS_DCE4(rdev))
+               evergreen_crtc_load_lut(crtc);
+       else if (ASIC_IS_AVIVO(rdev))
                avivo_crtc_load_lut(crtc);
        else
                legacy_crtc_load_lut(crtc);
@@ -234,7 +266,7 @@ static const char *encoder_names[34] = {
        "INTERNAL_UNIPHY2",
 };
 
-static const char *connector_names[13] = {
+static const char *connector_names[15] = {
        "Unknown",
        "VGA",
        "DVI-I",
@@ -248,6 +280,18 @@ static const char *connector_names[13] = {
        "DisplayPort",
        "HDMI-A",
        "HDMI-B",
+       "TV",
+       "eDP",
+};
+
+static const char *hpd_names[7] = {
+       "NONE",
+       "HPD1",
+       "HPD2",
+       "HPD3",
+       "HPD4",
+       "HPD5",
+       "HPD6",
 };
 
 static void radeon_print_display_setup(struct drm_device *dev)
@@ -264,16 +308,18 @@ static void radeon_print_display_setup(struct drm_device *dev)
                radeon_connector = to_radeon_connector(connector);
                DRM_INFO("Connector %d:\n", i);
                DRM_INFO("  %s\n", connector_names[connector->connector_type]);
+               if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
+                       DRM_INFO("  %s\n", hpd_names[radeon_connector->hpd.hpd]);
                if (radeon_connector->ddc_bus)
                        DRM_INFO("  DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                                 radeon_connector->ddc_bus->rec.mask_clk_reg,
                                 radeon_connector->ddc_bus->rec.mask_data_reg,
                                 radeon_connector->ddc_bus->rec.a_clk_reg,
                                 radeon_connector->ddc_bus->rec.a_data_reg,
-                                radeon_connector->ddc_bus->rec.put_clk_reg,
-                                radeon_connector->ddc_bus->rec.put_data_reg,
-                                radeon_connector->ddc_bus->rec.get_clk_reg,
-                                radeon_connector->ddc_bus->rec.get_data_reg);
+                                radeon_connector->ddc_bus->rec.en_clk_reg,
+                                radeon_connector->ddc_bus->rec.en_data_reg,
+                                radeon_connector->ddc_bus->rec.y_clk_reg,
+                                radeon_connector->ddc_bus->rec.y_data_reg);
                DRM_INFO("  Encoders:\n");
                list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                        radeon_encoder = to_radeon_encoder(encoder);
@@ -317,13 +363,17 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
                                ret = radeon_get_atom_connector_info_from_object_table(dev);
                        else
                                ret = radeon_get_atom_connector_info_from_supported_devices_table(dev);
-               } else
+               } else {
                        ret = radeon_get_legacy_connector_info_from_bios(dev);
+                       if (ret == false)
+                               ret = radeon_get_legacy_connector_info_from_table(dev);
+               }
        } else {
                if (!ASIC_IS_AVIVO(rdev))
                        ret = radeon_get_legacy_connector_info_from_table(dev);
        }
        if (ret) {
+               radeon_setup_encoder_clones(dev);
                radeon_print_display_setup(dev);
                list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head)
                        radeon_ddc_dump(drm_connector);
@@ -334,16 +384,25 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
 
 int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
 {
+       struct drm_device *dev = radeon_connector->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
        int ret = 0;
 
+       if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
+           (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
+               struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+               if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
+                    dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
+                       radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
+       }
        if (!radeon_connector->ddc_bus)
                return -1;
        if (!radeon_connector->edid) {
-               radeon_i2c_do_lock(radeon_connector, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector, 0);
        }
-
+       /* some servers provide a hardcoded edid in rom for KVMs */
+       if (!radeon_connector->edid)
+               radeon_connector->edid = radeon_combios_get_hardcoded_edid(rdev);
        if (radeon_connector->edid) {
                drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
                ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
@@ -361,9 +420,7 @@ static int radeon_ddc_dump(struct drm_connector *connector)
 
        if (!radeon_connector->ddc_bus)
                return -1;
-       radeon_i2c_do_lock(radeon_connector, 1);
        edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
-       radeon_i2c_do_lock(radeon_connector, 0);
        if (edid) {
                kfree(edid);
        }
@@ -380,17 +437,18 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d)
        return n;
 }
 
-void radeon_compute_pll(struct radeon_pll *pll,
-                       uint64_t freq,
-                       uint32_t *dot_clock_p,
-                       uint32_t *fb_div_p,
-                       uint32_t *frac_fb_div_p,
-                       uint32_t *ref_div_p,
-                       uint32_t *post_div_p,
-                       int flags)
+static void radeon_compute_pll_legacy(struct radeon_pll *pll,
+                                     uint64_t freq,
+                                     uint32_t *dot_clock_p,
+                                     uint32_t *fb_div_p,
+                                     uint32_t *frac_fb_div_p,
+                                     uint32_t *ref_div_p,
+                                     uint32_t *post_div_p)
 {
        uint32_t min_ref_div = pll->min_ref_div;
        uint32_t max_ref_div = pll->max_ref_div;
+       uint32_t min_post_div = pll->min_post_div;
+       uint32_t max_post_div = pll->max_post_div;
        uint32_t min_fractional_feed_div = 0;
        uint32_t max_fractional_feed_div = 0;
        uint32_t best_vco = pll->best_vco;
@@ -406,7 +464,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
        DRM_DEBUG("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div);
        freq = freq * 1000;
 
-       if (flags & RADEON_PLL_USE_REF_DIV)
+       if (pll->flags & RADEON_PLL_USE_REF_DIV)
                min_ref_div = max_ref_div = pll->reference_div;
        else {
                while (min_ref_div < max_ref_div-1) {
@@ -421,19 +479,22 @@ void radeon_compute_pll(struct radeon_pll *pll,
                }
        }
 
-       if (flags & RADEON_PLL_USE_FRAC_FB_DIV) {
+       if (pll->flags & RADEON_PLL_USE_POST_DIV)
+               min_post_div = max_post_div = pll->post_div;
+
+       if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
                min_fractional_feed_div = pll->min_frac_feedback_div;
                max_fractional_feed_div = pll->max_frac_feedback_div;
        }
 
-       for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) {
+       for (post_div = min_post_div; post_div <= max_post_div; ++post_div) {
                uint32_t ref_div;
 
-               if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
+               if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
                        continue;
 
                /* legacy radeons only have a few post_divs */
-               if (flags & RADEON_PLL_LEGACY) {
+               if (pll->flags & RADEON_PLL_LEGACY) {
                        if ((post_div == 5) ||
                            (post_div == 7) ||
                            (post_div == 9) ||
@@ -480,7 +541,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
                                        tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div;
                                        current_freq = radeon_div(tmp, ref_div * post_div);
 
-                                       if (flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
+                                       if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
                                                error = freq - current_freq;
                                                error = error < 0 ? 0xffffffff : error;
                                        } else
@@ -507,12 +568,12 @@ void radeon_compute_pll(struct radeon_pll *pll,
                                                        best_freq = current_freq;
                                                        best_error = error;
                                                        best_vco_diff = vco_diff;
-                                               } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
-                                                          ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
-                                                          ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
-                                                          ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
-                                                          ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
-                                                          ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
+                                               } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
+                                                          ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
+                                                          ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
+                                                          ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
+                                                          ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
+                                                          ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
                                                        best_post_div = post_div;
                                                        best_ref_div = ref_div;
                                                        best_feedback_div = feedback_div;
@@ -542,6 +603,118 @@ void radeon_compute_pll(struct radeon_pll *pll,
        *post_div_p = best_post_div;
 }
 
+static void radeon_compute_pll_avivo(struct radeon_pll *pll,
+                                    uint64_t freq,
+                                    uint32_t *dot_clock_p,
+                                    uint32_t *fb_div_p,
+                                    uint32_t *frac_fb_div_p,
+                                    uint32_t *ref_div_p,
+                                    uint32_t *post_div_p)
+{
+       fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq;
+       fixed20_12 pll_out_max, pll_out_min;
+       fixed20_12 pll_in_max, pll_in_min;
+       fixed20_12 reference_freq;
+       fixed20_12 error, ffreq, a, b;
+
+       pll_out_max.full = rfixed_const(pll->pll_out_max);
+       pll_out_min.full = rfixed_const(pll->pll_out_min);
+       pll_in_max.full = rfixed_const(pll->pll_in_max);
+       pll_in_min.full = rfixed_const(pll->pll_in_min);
+       reference_freq.full = rfixed_const(pll->reference_freq);
+       do_div(freq, 10);
+       ffreq.full = rfixed_const(freq);
+       error.full = rfixed_const(100 * 100);
+
+       /* max p */
+       p.full = rfixed_div(pll_out_max, ffreq);
+       p.full = rfixed_floor(p);
+
+       /* min m */
+       m.full = rfixed_div(reference_freq, pll_in_max);
+       m.full = rfixed_ceil(m);
+
+       while (1) {
+               n.full = rfixed_div(ffreq, reference_freq);
+               n.full = rfixed_mul(n, m);
+               n.full = rfixed_mul(n, p);
+
+               f_vco.full = rfixed_div(n, m);
+               f_vco.full = rfixed_mul(f_vco, reference_freq);
+
+               f_pclk.full = rfixed_div(f_vco, p);
+
+               if (f_pclk.full > ffreq.full)
+                       error.full = f_pclk.full - ffreq.full;
+               else
+                       error.full = ffreq.full - f_pclk.full;
+               error.full = rfixed_div(error, f_pclk);
+               a.full = rfixed_const(100 * 100);
+               error.full = rfixed_mul(error, a);
+
+               a.full = rfixed_mul(m, p);
+               a.full = rfixed_div(n, a);
+               best_freq.full = rfixed_mul(reference_freq, a);
+
+               if (rfixed_trunc(error) < 25)
+                       break;
+
+               a.full = rfixed_const(1);
+               m.full = m.full + a.full;
+               a.full = rfixed_div(reference_freq, m);
+               if (a.full >= pll_in_min.full)
+                       continue;
+
+               m.full = rfixed_div(reference_freq, pll_in_max);
+               m.full = rfixed_ceil(m);
+               a.full= rfixed_const(1);
+               p.full = p.full - a.full;
+               a.full = rfixed_mul(p, ffreq);
+               if (a.full >= pll_out_min.full)
+                       continue;
+               else {
+                       DRM_ERROR("Unable to find pll dividers\n");
+                       break;
+               }
+       }
+
+       a.full = rfixed_const(10);
+       b.full = rfixed_mul(n, a);
+
+       frac_n.full = rfixed_floor(n);
+       frac_n.full = rfixed_mul(frac_n, a);
+       frac_n.full = b.full - frac_n.full;
+
+       *dot_clock_p = rfixed_trunc(best_freq);
+       *fb_div_p = rfixed_trunc(n);
+       *frac_fb_div_p = rfixed_trunc(frac_n);
+       *ref_div_p = rfixed_trunc(m);
+       *post_div_p = rfixed_trunc(p);
+
+       DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
+}
+
+void radeon_compute_pll(struct radeon_pll *pll,
+                       uint64_t freq,
+                       uint32_t *dot_clock_p,
+                       uint32_t *fb_div_p,
+                       uint32_t *frac_fb_div_p,
+                       uint32_t *ref_div_p,
+                       uint32_t *post_div_p)
+{
+       switch (pll->algo) {
+       case PLL_ALGO_AVIVO:
+               radeon_compute_pll_avivo(pll, freq, dot_clock_p, fb_div_p,
+                                        frac_fb_div_p, ref_div_p, post_div_p);
+               break;
+       case PLL_ALGO_LEGACY:
+       default:
+               radeon_compute_pll_legacy(pll, freq, dot_clock_p, fb_div_p,
+                                         frac_fb_div_p, ref_div_p, post_div_p);
+               break;
+       }
+}
+
 static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
        struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
@@ -551,7 +724,6 @@ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
                radeonfb_remove(dev, fb);
 
        if (radeon_fb->obj) {
-               radeon_gem_object_unpin(radeon_fb->obj);
                mutex_lock(&dev->struct_mutex);
                drm_gem_object_unreference(radeon_fb->obj);
                mutex_unlock(&dev->struct_mutex);
@@ -599,7 +771,11 @@ radeon_user_framebuffer_create(struct drm_device *dev,
        struct drm_gem_object *obj;
 
        obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
-
+       if (obj ==  NULL) {
+               dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
+                       "can't create framebuffer\n", mode_cmd->handle);
+               return NULL;
+       }
        return radeon_framebuffer_create(dev, mode_cmd, obj);
 }
 
@@ -629,7 +805,7 @@ static struct drm_prop_enum_list radeon_tv_std_enum_list[] =
        { TV_STD_SECAM, "secam" },
 };
 
-int radeon_modeset_create_props(struct radeon_device *rdev)
+static int radeon_modeset_create_props(struct radeon_device *rdev)
 {
        int i, sz;
 
@@ -642,7 +818,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
                        return -ENOMEM;
 
                rdev->mode_info.coherent_mode_property->values[0] = 0;
-               rdev->mode_info.coherent_mode_property->values[0] = 1;
+               rdev->mode_info.coherent_mode_property->values[1] = 1;
        }
 
        if (!ASIC_IS_AVIVO(rdev)) {
@@ -666,7 +842,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
        if (!rdev->mode_info.load_detect_property)
                return -ENOMEM;
        rdev->mode_info.load_detect_property->values[0] = 0;
-       rdev->mode_info.load_detect_property->values[0] = 1;
+       rdev->mode_info.load_detect_property->values[1] = 1;
 
        drm_mode_create_scaling_mode_property(rdev->ddev);
 
@@ -687,7 +863,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
 
 int radeon_modeset_init(struct radeon_device *rdev)
 {
-       int num_crtc = 2, i;
+       int i;
        int ret;
 
        drm_mode_config_init(rdev->ddev);
@@ -710,11 +886,23 @@ int radeon_modeset_init(struct radeon_device *rdev)
                return ret;
        }
 
+       /* check combios for a valid hardcoded EDID - Sun servers */
+       if (!rdev->is_atom_bios) {
+               /* check for hardcoded EDID in BIOS */
+               radeon_combios_check_hardcoded_edid(rdev);
+       }
+
        if (rdev->flags & RADEON_SINGLE_CRTC)
-               num_crtc = 1;
+               rdev->num_crtc = 1;
+       else {
+               if (ASIC_IS_DCE4(rdev))
+                       rdev->num_crtc = 6;
+               else
+                       rdev->num_crtc = 2;
+       }
 
        /* allocate crtcs */
-       for (i = 0; i < num_crtc; i++) {
+       for (i = 0; i < rdev->num_crtc; i++) {
                radeon_crtc_init(rdev->ddev, i);
        }
 
@@ -723,13 +911,18 @@ int radeon_modeset_init(struct radeon_device *rdev)
        if (!ret) {
                return ret;
        }
+       /* initialize hpd */
+       radeon_hpd_init(rdev);
        drm_helper_initial_config(rdev->ddev);
        return 0;
 }
 
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
+       kfree(rdev->mode_info.bios_hardcoded_edid);
+
        if (rdev->mode_info.mode_config_initialized) {
+               radeon_hpd_fini(rdev);
                drm_mode_config_cleanup(rdev->ddev);
                rdev->mode_info.mode_config_initialized = false;
        }
@@ -750,9 +943,17 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
                if (first) {
-                       radeon_crtc->rmx_type = radeon_encoder->rmx_type;
+                       /* set scaling */
+                       if (radeon_encoder->rmx_type == RMX_OFF)
+                               radeon_crtc->rmx_type = RMX_OFF;
+                       else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay ||
+                                mode->vdisplay < radeon_encoder->native_mode.vdisplay)
+                               radeon_crtc->rmx_type = radeon_encoder->rmx_type;
+                       else
+                               radeon_crtc->rmx_type = RMX_OFF;
+                       /* copy native mode */
                        memcpy(&radeon_crtc->native_mode,
-                               &radeon_encoder->native_mode,
+                              &radeon_encoder->native_mode,
                                sizeof(struct drm_display_mode));
                        first = false;
                } else {
This page took 0.055812 seconds and 5 git commands to generate.