2021-01-14 13:13:46 +02:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/*
|
|
|
|
* Copyright © 2020 Intel Corporation
|
|
|
|
*/
|
2021-09-30 12:22:59 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
#include <linux/kernel.h>
|
2022-02-25 15:46:31 -08:00
|
|
|
#include <linux/string_helpers.h>
|
2021-09-30 12:22:59 +03:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
#include <drm/drm_print.h>
|
|
|
|
|
2023-10-11 00:01:01 +05:30
|
|
|
#include "intel_atomic.h"
|
2021-02-05 16:48:40 +02:00
|
|
|
#include "intel_crtc.h"
|
drm/i915/mtl: Add Support for C10 PHY message bus and pll programming
XELPDP has C10 and C20 phys from Synopsys to drive displays. Each phy
has a dedicated PIPE 5.2 Message bus for configuration. This message
bus is used to configure the phy internal registers.
XELPDP has C10 phys to drive output to the EDP and the native output
from the display engine. Add structures, programming hardware state
readout logic. Port clock calculations are similar to DG2. Use the DG2
formulae to calculate the port clock but use the relevant pll signals.
Note: PHY lane 0 is always used for PLL programming.
Add sequences for C10 phy enable/disable phy lane reset,
powerdown change sequence and phy lane programming.
Bspec: 64539, 64568, 64599, 65100, 65101, 65450, 65451, 67610, 67636
v2: Squash patches related to C10 phy message bus and pll
programming support (Jani)
Move register definitions to a new file i.e. intel_cx0_reg_defs.h (Jani)
Move macro definitions (Jani)
DP rates as separate patch (Jani)
Spin out xelpdp register definitions into a separate file (Jani)
Replace macro to select registers based on phy lane with
function calls (Jani)
Fix styling issues (Jani)
Call XELPDP_PORT_P2M_MSGBUS_STATUS() with port instead of phy (Lucas)
v3: Move clear request flag into try-loop
v4: On PHY idle change drm_err_once() as drm_dbg_kms() (Jani)
use __intel_de_wait_for_register() instead of __intel_wait_for_register
and uncomment intel_uncore.h (Jani)
Add DP-alt support for PHY lane programming (Khaled)
v4: Add tx and cmn on c10mpllb_state (Imre)
Add missing waits for pending transactions between two message bus
writes (Imre)
General cleanups and simplifications (Imre)
v5: Few nit cleanups from rev4 (imre)
s/dev_priv/i915/ , s/c10mpllb/c10pll/ (RK)
Rebase
v6: Move the mtl code from intel_c10pll_calc_port_clock to mtl function
Fix typo in comment for REG_FIELD_PREP8 definition(Imre)
Cc: Mika Kahola <mika.kahola@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Gustavo Sousa <gustavo.sousa@intel.com>
Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com>
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com> (v4)
Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-4-radhakrishna.sripada@intel.com
2023-04-13 14:24:37 -07:00
|
|
|
#include "intel_cx0_phy.h"
|
2021-04-30 17:39:44 +03:00
|
|
|
#include "intel_de.h"
|
2021-01-14 13:13:46 +02:00
|
|
|
#include "intel_display.h"
|
2025-06-06 13:22:56 +03:00
|
|
|
#include "intel_display_regs.h"
|
2021-09-30 12:22:59 +03:00
|
|
|
#include "intel_display_types.h"
|
2022-11-02 12:08:13 +02:00
|
|
|
#include "intel_dpio_phy.h"
|
2021-01-14 13:13:46 +02:00
|
|
|
#include "intel_dpll.h"
|
|
|
|
#include "intel_lvds.h"
|
2023-11-14 12:45:32 +02:00
|
|
|
#include "intel_lvds_regs.h"
|
2021-01-14 13:13:46 +02:00
|
|
|
#include "intel_panel.h"
|
2021-09-30 12:22:59 +03:00
|
|
|
#include "intel_pps.h"
|
|
|
|
#include "intel_snps_phy.h"
|
2024-04-30 21:19:06 +03:00
|
|
|
#include "vlv_dpio_phy_regs.h"
|
2025-05-12 17:56:56 +03:00
|
|
|
#include "vlv_sideband.h"
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
struct intel_dpll_global_funcs {
|
2022-03-25 14:31:56 +02:00
|
|
|
int (*crtc_compute_clock)(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc);
|
2025-05-15 12:47:55 +05:30
|
|
|
int (*crtc_get_dpll)(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc);
|
2022-02-03 16:02:32 +02:00
|
|
|
};
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
struct intel_limit {
|
|
|
|
struct {
|
|
|
|
int min, max;
|
|
|
|
} dot, vco, n, m, m1, m2, p, p1;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int dot_limit;
|
|
|
|
int p2_slow, p2_fast;
|
|
|
|
} p2;
|
|
|
|
};
|
|
|
|
static const struct intel_limit intel_limits_i8xx_dac = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 908000, .max = 1512000 },
|
|
|
|
.n = { .min = 2, .max = 16 },
|
|
|
|
.m = { .min = 96, .max = 140 },
|
|
|
|
.m1 = { .min = 18, .max = 26 },
|
|
|
|
.m2 = { .min = 6, .max = 16 },
|
|
|
|
.p = { .min = 4, .max = 128 },
|
|
|
|
.p1 = { .min = 2, .max = 33 },
|
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
|
.p2_slow = 4, .p2_fast = 2 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_i8xx_dvo = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 908000, .max = 1512000 },
|
|
|
|
.n = { .min = 2, .max = 16 },
|
|
|
|
.m = { .min = 96, .max = 140 },
|
|
|
|
.m1 = { .min = 18, .max = 26 },
|
|
|
|
.m2 = { .min = 6, .max = 16 },
|
|
|
|
.p = { .min = 4, .max = 128 },
|
|
|
|
.p1 = { .min = 2, .max = 33 },
|
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
|
.p2_slow = 4, .p2_fast = 4 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_i8xx_lvds = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 908000, .max = 1512000 },
|
|
|
|
.n = { .min = 2, .max = 16 },
|
|
|
|
.m = { .min = 96, .max = 140 },
|
|
|
|
.m1 = { .min = 18, .max = 26 },
|
|
|
|
.m2 = { .min = 6, .max = 16 },
|
|
|
|
.p = { .min = 4, .max = 128 },
|
|
|
|
.p1 = { .min = 1, .max = 6 },
|
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
|
.p2_slow = 14, .p2_fast = 7 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_i9xx_sdvo = {
|
|
|
|
.dot = { .min = 20000, .max = 400000 },
|
|
|
|
.vco = { .min = 1400000, .max = 2800000 },
|
|
|
|
.n = { .min = 1, .max = 6 },
|
|
|
|
.m = { .min = 70, .max = 120 },
|
|
|
|
.m1 = { .min = 8, .max = 18 },
|
|
|
|
.m2 = { .min = 3, .max = 7 },
|
|
|
|
.p = { .min = 5, .max = 80 },
|
|
|
|
.p1 = { .min = 1, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 200000,
|
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_i9xx_lvds = {
|
|
|
|
.dot = { .min = 20000, .max = 400000 },
|
|
|
|
.vco = { .min = 1400000, .max = 2800000 },
|
|
|
|
.n = { .min = 1, .max = 6 },
|
|
|
|
.m = { .min = 70, .max = 120 },
|
|
|
|
.m1 = { .min = 8, .max = 18 },
|
|
|
|
.m2 = { .min = 3, .max = 7 },
|
|
|
|
.p = { .min = 7, .max = 98 },
|
|
|
|
.p1 = { .min = 1, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 112000,
|
|
|
|
.p2_slow = 14, .p2_fast = 7 },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_g4x_sdvo = {
|
|
|
|
.dot = { .min = 25000, .max = 270000 },
|
|
|
|
.vco = { .min = 1750000, .max = 3500000},
|
|
|
|
.n = { .min = 1, .max = 4 },
|
|
|
|
.m = { .min = 104, .max = 138 },
|
|
|
|
.m1 = { .min = 17, .max = 23 },
|
|
|
|
.m2 = { .min = 5, .max = 11 },
|
|
|
|
.p = { .min = 10, .max = 30 },
|
|
|
|
.p1 = { .min = 1, .max = 3},
|
|
|
|
.p2 = { .dot_limit = 270000,
|
|
|
|
.p2_slow = 10,
|
|
|
|
.p2_fast = 10
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_g4x_hdmi = {
|
|
|
|
.dot = { .min = 22000, .max = 400000 },
|
|
|
|
.vco = { .min = 1750000, .max = 3500000},
|
|
|
|
.n = { .min = 1, .max = 4 },
|
|
|
|
.m = { .min = 104, .max = 138 },
|
|
|
|
.m1 = { .min = 16, .max = 23 },
|
|
|
|
.m2 = { .min = 5, .max = 11 },
|
|
|
|
.p = { .min = 5, .max = 80 },
|
|
|
|
.p1 = { .min = 1, .max = 8},
|
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_g4x_single_channel_lvds = {
|
|
|
|
.dot = { .min = 20000, .max = 115000 },
|
|
|
|
.vco = { .min = 1750000, .max = 3500000 },
|
|
|
|
.n = { .min = 1, .max = 3 },
|
|
|
|
.m = { .min = 104, .max = 138 },
|
|
|
|
.m1 = { .min = 17, .max = 23 },
|
|
|
|
.m2 = { .min = 5, .max = 11 },
|
|
|
|
.p = { .min = 28, .max = 112 },
|
|
|
|
.p1 = { .min = 2, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 0,
|
|
|
|
.p2_slow = 14, .p2_fast = 14
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_g4x_dual_channel_lvds = {
|
|
|
|
.dot = { .min = 80000, .max = 224000 },
|
|
|
|
.vco = { .min = 1750000, .max = 3500000 },
|
|
|
|
.n = { .min = 1, .max = 3 },
|
|
|
|
.m = { .min = 104, .max = 138 },
|
|
|
|
.m1 = { .min = 17, .max = 23 },
|
|
|
|
.m2 = { .min = 5, .max = 11 },
|
|
|
|
.p = { .min = 14, .max = 42 },
|
|
|
|
.p1 = { .min = 2, .max = 6 },
|
|
|
|
.p2 = { .dot_limit = 0,
|
|
|
|
.p2_slow = 7, .p2_fast = 7
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit pnv_limits_sdvo = {
|
|
|
|
.dot = { .min = 20000, .max = 400000},
|
|
|
|
.vco = { .min = 1700000, .max = 3500000 },
|
|
|
|
/* Pineview's Ncounter is a ring counter */
|
|
|
|
.n = { .min = 3, .max = 6 },
|
|
|
|
.m = { .min = 2, .max = 256 },
|
|
|
|
/* Pineview only has one combined m divider, which we treat as m2. */
|
|
|
|
.m1 = { .min = 0, .max = 0 },
|
|
|
|
.m2 = { .min = 0, .max = 254 },
|
|
|
|
.p = { .min = 5, .max = 80 },
|
|
|
|
.p1 = { .min = 1, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 200000,
|
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit pnv_limits_lvds = {
|
|
|
|
.dot = { .min = 20000, .max = 400000 },
|
|
|
|
.vco = { .min = 1700000, .max = 3500000 },
|
|
|
|
.n = { .min = 3, .max = 6 },
|
|
|
|
.m = { .min = 2, .max = 256 },
|
|
|
|
.m1 = { .min = 0, .max = 0 },
|
|
|
|
.m2 = { .min = 0, .max = 254 },
|
|
|
|
.p = { .min = 7, .max = 112 },
|
|
|
|
.p1 = { .min = 1, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 112000,
|
|
|
|
.p2_slow = 14, .p2_fast = 14 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Ironlake / Sandybridge
|
|
|
|
*
|
|
|
|
* We calculate clock using (register_value + 2) for N/M1/M2, so here
|
|
|
|
* the range value for them is (actual_value - 2).
|
|
|
|
*/
|
|
|
|
static const struct intel_limit ilk_limits_dac = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 1760000, .max = 3510000 },
|
|
|
|
.n = { .min = 1, .max = 5 },
|
|
|
|
.m = { .min = 79, .max = 127 },
|
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
|
.p = { .min = 5, .max = 80 },
|
|
|
|
.p1 = { .min = 1, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit ilk_limits_single_lvds = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 1760000, .max = 3510000 },
|
|
|
|
.n = { .min = 1, .max = 3 },
|
|
|
|
.m = { .min = 79, .max = 118 },
|
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
|
.p = { .min = 28, .max = 112 },
|
|
|
|
.p1 = { .min = 2, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
|
.p2_slow = 14, .p2_fast = 14 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit ilk_limits_dual_lvds = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 1760000, .max = 3510000 },
|
|
|
|
.n = { .min = 1, .max = 3 },
|
|
|
|
.m = { .min = 79, .max = 127 },
|
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
|
.p = { .min = 14, .max = 56 },
|
|
|
|
.p1 = { .min = 2, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
|
.p2_slow = 7, .p2_fast = 7 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* LVDS 100mhz refclk limits. */
|
|
|
|
static const struct intel_limit ilk_limits_single_lvds_100m = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 1760000, .max = 3510000 },
|
|
|
|
.n = { .min = 1, .max = 2 },
|
|
|
|
.m = { .min = 79, .max = 126 },
|
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
|
.p = { .min = 28, .max = 112 },
|
|
|
|
.p1 = { .min = 2, .max = 8 },
|
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
|
.p2_slow = 14, .p2_fast = 14 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit ilk_limits_dual_lvds_100m = {
|
|
|
|
.dot = { .min = 25000, .max = 350000 },
|
|
|
|
.vco = { .min = 1760000, .max = 3510000 },
|
|
|
|
.n = { .min = 1, .max = 3 },
|
|
|
|
.m = { .min = 79, .max = 126 },
|
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
|
.p = { .min = 14, .max = 42 },
|
|
|
|
.p1 = { .min = 2, .max = 6 },
|
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
|
.p2_slow = 7, .p2_fast = 7 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_vlv = {
|
|
|
|
/*
|
2022-03-09 23:43:01 +02:00
|
|
|
* These are based on the data rate limits (measured in fast clocks)
|
2021-01-14 13:13:46 +02:00
|
|
|
* since those are the strictest limits we have. The fast
|
|
|
|
* clock and actual rate limits are more relaxed, so checking
|
|
|
|
* them would make no difference.
|
|
|
|
*/
|
2022-03-09 23:43:01 +02:00
|
|
|
.dot = { .min = 25000, .max = 270000 },
|
2021-01-14 13:13:46 +02:00
|
|
|
.vco = { .min = 4000000, .max = 6000000 },
|
|
|
|
.n = { .min = 1, .max = 7 },
|
|
|
|
.m1 = { .min = 2, .max = 3 },
|
|
|
|
.m2 = { .min = 11, .max = 156 },
|
|
|
|
.p1 = { .min = 2, .max = 3 },
|
|
|
|
.p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_chv = {
|
|
|
|
/*
|
2022-03-09 23:43:01 +02:00
|
|
|
* These are based on the data rate limits (measured in fast clocks)
|
2021-01-14 13:13:46 +02:00
|
|
|
* since those are the strictest limits we have. The fast
|
|
|
|
* clock and actual rate limits are more relaxed, so checking
|
|
|
|
* them would make no difference.
|
|
|
|
*/
|
2022-03-09 23:43:01 +02:00
|
|
|
.dot = { .min = 25000, .max = 540000 },
|
2021-01-14 13:13:46 +02:00
|
|
|
.vco = { .min = 4800000, .max = 6480000 },
|
|
|
|
.n = { .min = 1, .max = 1 },
|
|
|
|
.m1 = { .min = 2, .max = 2 },
|
|
|
|
.m2 = { .min = 24 << 22, .max = 175 << 22 },
|
|
|
|
.p1 = { .min = 2, .max = 4 },
|
|
|
|
.p2 = { .p2_slow = 1, .p2_fast = 14 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct intel_limit intel_limits_bxt = {
|
2022-03-08 01:39:39 +02:00
|
|
|
.dot = { .min = 25000, .max = 594000 },
|
2021-01-14 13:13:46 +02:00
|
|
|
.vco = { .min = 4800000, .max = 6700000 },
|
|
|
|
.n = { .min = 1, .max = 1 },
|
|
|
|
.m1 = { .min = 2, .max = 2 },
|
|
|
|
/* FIXME: find real m2 limits */
|
|
|
|
.m2 = { .min = 2 << 22, .max = 255 << 22 },
|
|
|
|
.p1 = { .min = 2, .max = 4 },
|
|
|
|
.p2 = { .p2_slow = 1, .p2_fast = 20 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Platform specific helpers to calculate the port PLL loopback- (clock.m),
|
|
|
|
* and post-divider (clock.p) values, pre- (clock.vco) and post-divided fast
|
|
|
|
* (clock.dot) clock rates. This fast dot clock is fed to the port's IO logic.
|
|
|
|
* The helpers' return value is the rate of the clock that is fed to the
|
|
|
|
* display engine's pipe which can be the above fast dot clock rate or a
|
|
|
|
* divided-down version of it.
|
|
|
|
*/
|
|
|
|
/* m1 is reserved as 0 in Pineview, n is a ring counter */
|
2023-11-14 12:45:32 +02:00
|
|
|
static int pnv_calc_dpll_params(int refclk, struct dpll *clock)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
|
|
|
clock->m = clock->m2 + 2;
|
|
|
|
clock->p = clock->p1 * clock->p2;
|
2023-07-05 23:21:13 +03:00
|
|
|
|
|
|
|
clock->vco = clock->n == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
|
|
|
|
clock->dot = clock->p == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(clock->vco, clock->p);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
return clock->dot;
|
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:21 +03:00
|
|
|
static u32 i9xx_dpll_compute_m(const struct dpll *dpll)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
|
|
|
return 5 * (dpll->m1 + 2) + (dpll->m2 + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i9xx_calc_dpll_params(int refclk, struct dpll *clock)
|
|
|
|
{
|
|
|
|
clock->m = i9xx_dpll_compute_m(clock);
|
|
|
|
clock->p = clock->p1 * clock->p2;
|
2023-07-05 23:21:13 +03:00
|
|
|
|
|
|
|
clock->vco = clock->n + 2 == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2);
|
|
|
|
clock->dot = clock->p == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(clock->vco, clock->p);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
return clock->dot;
|
|
|
|
}
|
|
|
|
|
2023-11-14 12:45:32 +02:00
|
|
|
static int vlv_calc_dpll_params(int refclk, struct dpll *clock)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
|
|
|
clock->m = clock->m1 * clock->m2;
|
2022-03-09 23:43:01 +02:00
|
|
|
clock->p = clock->p1 * clock->p2 * 5;
|
2023-07-05 23:21:13 +03:00
|
|
|
|
|
|
|
clock->vco = clock->n == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
|
|
|
|
clock->dot = clock->p == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(clock->vco, clock->p);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-03-09 23:43:01 +02:00
|
|
|
return clock->dot;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int chv_calc_dpll_params(int refclk, struct dpll *clock)
|
|
|
|
{
|
|
|
|
clock->m = clock->m1 * clock->m2;
|
2022-03-09 23:43:01 +02:00
|
|
|
clock->p = clock->p1 * clock->p2 * 5;
|
2023-07-05 23:21:13 +03:00
|
|
|
|
|
|
|
clock->vco = clock->n == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, clock->m), clock->n << 22);
|
|
|
|
clock->dot = clock->p == 0 ? 0 :
|
|
|
|
DIV_ROUND_CLOSEST(clock->vco, clock->p);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-03-09 23:43:01 +02:00
|
|
|
return clock->dot;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:57 +03:00
|
|
|
static int i9xx_pll_refclk(const struct intel_crtc_state *crtc_state)
|
2023-11-14 12:45:32 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2023-11-14 12:45:32 +02:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
if ((hw_state->dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN)
|
2025-03-25 14:36:33 +02:00
|
|
|
return display->vbt.lvds_ssc_freq;
|
2025-04-17 12:10:36 +03:00
|
|
|
else if (HAS_PCH_SPLIT(display))
|
2023-11-14 12:45:32 +02:00
|
|
|
return 120000;
|
2025-03-25 14:36:33 +02:00
|
|
|
else if (DISPLAY_VER(display) != 2)
|
2023-11-14 12:45:32 +02:00
|
|
|
return 96000;
|
|
|
|
else
|
|
|
|
return 48000;
|
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:52 +03:00
|
|
|
void i9xx_dpll_get_hw_state(struct intel_crtc *crtc,
|
2024-04-12 21:27:01 +03:00
|
|
|
struct intel_dpll_hw_state *dpll_hw_state)
|
2024-04-12 21:26:52 +03:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &dpll_hw_state->i9xx;
|
2024-04-12 21:26:52 +03:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) >= 4) {
|
2024-04-12 21:26:52 +03:00
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
/* No way to read it out on pipes B and C */
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.cherryview && crtc->pipe != PIPE_A)
|
|
|
|
tmp = display->state.chv_dpll_md[crtc->pipe];
|
2024-04-12 21:26:52 +03:00
|
|
|
else
|
2025-03-25 14:36:33 +02:00
|
|
|
tmp = intel_de_read(display,
|
|
|
|
DPLL_MD(display, crtc->pipe));
|
2024-04-12 21:26:52 +03:00
|
|
|
|
|
|
|
hw_state->dpll_md = tmp;
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
hw_state->dpll = intel_de_read(display, DPLL(display, crtc->pipe));
|
2024-04-12 21:26:52 +03:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!display->platform.valleyview && !display->platform.cherryview) {
|
|
|
|
hw_state->fp0 = intel_de_read(display, FP0(crtc->pipe));
|
|
|
|
hw_state->fp1 = intel_de_read(display, FP1(crtc->pipe));
|
2024-04-12 21:26:52 +03:00
|
|
|
} else {
|
|
|
|
/* Mask out read-only status bits. */
|
|
|
|
hw_state->dpll &= ~(DPLL_LOCK_VLV |
|
|
|
|
DPLL_PORTC_READY_MASK |
|
|
|
|
DPLL_PORTB_READY_MASK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-14 12:45:32 +02:00
|
|
|
/* Returns the clock of the currently programmed mode of the given pipe. */
|
2024-04-12 21:26:59 +03:00
|
|
|
void i9xx_crtc_clock_get(struct intel_crtc_state *crtc_state)
|
2023-11-14 12:45:32 +02:00
|
|
|
{
|
2025-03-21 12:52:51 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:26:59 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-12 21:27:00 +03:00
|
|
|
u32 dpll = hw_state->dpll;
|
2023-11-14 12:45:32 +02:00
|
|
|
u32 fp;
|
|
|
|
struct dpll clock;
|
|
|
|
int port_clock;
|
2024-04-12 21:26:59 +03:00
|
|
|
int refclk = i9xx_pll_refclk(crtc_state);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
|
|
|
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
|
2024-04-12 21:27:00 +03:00
|
|
|
fp = hw_state->fp0;
|
2023-11-14 12:45:32 +02:00
|
|
|
else
|
2024-04-12 21:27:00 +03:00
|
|
|
fp = hw_state->fp1;
|
2023-11-14 12:45:32 +02:00
|
|
|
|
|
|
|
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.pineview) {
|
2023-11-14 12:45:32 +02:00
|
|
|
clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
|
|
|
|
clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT;
|
|
|
|
} else {
|
|
|
|
clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
|
|
|
|
clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) != 2) {
|
|
|
|
if (display->platform.pineview)
|
2023-11-14 12:45:32 +02:00
|
|
|
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
|
|
|
|
DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
|
|
|
|
else
|
|
|
|
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
|
|
|
|
DPLL_FPA01_P1_POST_DIV_SHIFT);
|
|
|
|
|
|
|
|
switch (dpll & DPLL_MODE_MASK) {
|
|
|
|
case DPLLB_MODE_DAC_SERIAL:
|
|
|
|
clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
|
|
|
|
5 : 10;
|
|
|
|
break;
|
|
|
|
case DPLLB_MODE_LVDS:
|
|
|
|
clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
|
|
|
|
7 : 14;
|
|
|
|
break;
|
|
|
|
default:
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_dbg_kms(display->drm,
|
2023-11-14 12:45:32 +02:00
|
|
|
"Unknown DPLL mode %08x in programmed "
|
|
|
|
"mode\n", (int)(dpll & DPLL_MODE_MASK));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.pineview)
|
2023-11-14 12:45:32 +02:00
|
|
|
port_clock = pnv_calc_dpll_params(refclk, &clock);
|
|
|
|
else
|
|
|
|
port_clock = i9xx_calc_dpll_params(refclk, &clock);
|
|
|
|
} else {
|
|
|
|
enum pipe lvds_pipe;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.i85x &&
|
2025-03-21 12:52:51 +02:00
|
|
|
intel_lvds_port_enabled(display, LVDS, &lvds_pipe) &&
|
2023-11-14 12:45:32 +02:00
|
|
|
lvds_pipe == crtc->pipe) {
|
2025-03-25 14:36:33 +02:00
|
|
|
u32 lvds = intel_de_read(display, LVDS);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
|
|
|
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
|
|
|
|
DPLL_FPA01_P1_POST_DIV_SHIFT);
|
|
|
|
|
|
|
|
if (lvds & LVDS_CLKB_POWER_UP)
|
|
|
|
clock.p2 = 7;
|
|
|
|
else
|
|
|
|
clock.p2 = 14;
|
|
|
|
} else {
|
|
|
|
if (dpll & PLL_P1_DIVIDE_BY_TWO)
|
|
|
|
clock.p1 = 2;
|
|
|
|
else {
|
|
|
|
clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
|
|
|
|
DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
|
|
|
|
}
|
|
|
|
if (dpll & PLL_P2_DIVIDE_BY_4)
|
|
|
|
clock.p2 = 4;
|
|
|
|
else
|
|
|
|
clock.p2 = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
port_clock = i9xx_calc_dpll_params(refclk, &clock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This value includes pixel_multiplier. We will use
|
|
|
|
* port_clock to compute adjusted_mode.crtc_clock in the
|
|
|
|
* encoder's get_config() function.
|
|
|
|
*/
|
2024-04-12 21:26:59 +03:00
|
|
|
crtc_state->port_clock = port_clock;
|
2023-11-14 12:45:32 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:59 +03:00
|
|
|
void vlv_crtc_clock_get(struct intel_crtc_state *crtc_state)
|
2023-11-14 12:45:32 +02:00
|
|
|
{
|
2025-05-12 17:56:57 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:26:59 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-22 11:34:51 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(crtc->pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2023-11-14 12:45:32 +02:00
|
|
|
int refclk = 100000;
|
2024-04-22 11:34:49 +03:00
|
|
|
struct dpll clock;
|
|
|
|
u32 tmp;
|
2023-11-14 12:45:32 +02:00
|
|
|
|
|
|
|
/* In case of DSI, DPLL will not be used */
|
2024-04-12 21:27:00 +03:00
|
|
|
if ((hw_state->dpll & DPLL_VCO_ENABLE) == 0)
|
2023-11-14 12:45:32 +02:00
|
|
|
return;
|
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_PLL_DW3(ch));
|
|
|
|
vlv_dpio_put(display->drm);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
2024-04-22 11:34:56 +03:00
|
|
|
clock.m1 = REG_FIELD_GET(DPIO_M1_DIV_MASK, tmp);
|
|
|
|
clock.m2 = REG_FIELD_GET(DPIO_M2_DIV_MASK, tmp);
|
|
|
|
clock.n = REG_FIELD_GET(DPIO_N_DIV_MASK, tmp);
|
|
|
|
clock.p1 = REG_FIELD_GET(DPIO_P1_DIV_MASK, tmp);
|
|
|
|
clock.p2 = REG_FIELD_GET(DPIO_P2_DIV_MASK, tmp);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
2024-04-12 21:26:59 +03:00
|
|
|
crtc_state->port_clock = vlv_calc_dpll_params(refclk, &clock);
|
2023-11-14 12:45:32 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:59 +03:00
|
|
|
void chv_crtc_clock_get(struct intel_crtc_state *crtc_state)
|
2023-11-14 12:45:32 +02:00
|
|
|
{
|
2025-05-12 17:56:57 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:26:59 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-22 11:34:50 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(crtc->pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2023-11-14 12:45:32 +02:00
|
|
|
struct dpll clock;
|
|
|
|
u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2, pll_dw3;
|
|
|
|
int refclk = 100000;
|
|
|
|
|
|
|
|
/* In case of DSI, DPLL will not be used */
|
2024-04-12 21:27:00 +03:00
|
|
|
if ((hw_state->dpll & DPLL_VCO_ENABLE) == 0)
|
2023-11-14 12:45:32 +02:00
|
|
|
return;
|
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
|
|
|
cmn_dw13 = vlv_dpio_read(display->drm, phy, CHV_CMN_DW13(ch));
|
|
|
|
pll_dw0 = vlv_dpio_read(display->drm, phy, CHV_PLL_DW0(ch));
|
|
|
|
pll_dw1 = vlv_dpio_read(display->drm, phy, CHV_PLL_DW1(ch));
|
|
|
|
pll_dw2 = vlv_dpio_read(display->drm, phy, CHV_PLL_DW2(ch));
|
|
|
|
pll_dw3 = vlv_dpio_read(display->drm, phy, CHV_PLL_DW3(ch));
|
|
|
|
vlv_dpio_put(display->drm);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
2024-04-22 11:34:56 +03:00
|
|
|
clock.m1 = REG_FIELD_GET(DPIO_CHV_M1_DIV_MASK, pll_dw1) == DPIO_CHV_M1_DIV_BY_2 ? 2 : 0;
|
|
|
|
clock.m2 = REG_FIELD_GET(DPIO_CHV_M2_DIV_MASK, pll_dw0) << 22;
|
2023-11-14 12:45:32 +02:00
|
|
|
if (pll_dw3 & DPIO_CHV_FRAC_DIV_EN)
|
2024-04-22 11:34:56 +03:00
|
|
|
clock.m2 |= REG_FIELD_GET(DPIO_CHV_M2_FRAC_DIV_MASK, pll_dw2);
|
|
|
|
clock.n = REG_FIELD_GET(DPIO_CHV_N_DIV_MASK, pll_dw1);
|
|
|
|
clock.p1 = REG_FIELD_GET(DPIO_CHV_P1_DIV_MASK, cmn_dw13);
|
|
|
|
clock.p2 = REG_FIELD_GET(DPIO_CHV_P2_DIV_MASK, cmn_dw13);
|
2023-11-14 12:45:32 +02:00
|
|
|
|
2024-04-12 21:26:59 +03:00
|
|
|
crtc_state->port_clock = chv_calc_dpll_params(refclk, &clock);
|
2023-11-14 12:45:32 +02:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
/*
|
|
|
|
* Returns whether the given set of divisors are valid for a given refclk with
|
|
|
|
* the given connectors.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
static bool intel_pll_is_valid(struct intel_display *display,
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit,
|
|
|
|
const struct dpll *clock)
|
|
|
|
{
|
|
|
|
if (clock->n < limit->n.min || limit->n.max < clock->n)
|
|
|
|
return false;
|
|
|
|
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
|
|
|
|
return false;
|
|
|
|
if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
|
|
|
|
return false;
|
|
|
|
if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
|
|
|
|
return false;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!display->platform.pineview &&
|
|
|
|
!display->platform.valleyview && !display->platform.cherryview &&
|
|
|
|
!display->platform.broxton && !display->platform.geminilake)
|
2021-01-14 13:13:46 +02:00
|
|
|
if (clock->m1 <= clock->m2)
|
|
|
|
return false;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!display->platform.valleyview && !display->platform.cherryview &&
|
|
|
|
!display->platform.broxton && !display->platform.geminilake) {
|
2021-01-14 13:13:46 +02:00
|
|
|
if (clock->p < limit->p.min || limit->p.max < clock->p)
|
|
|
|
return false;
|
|
|
|
if (clock->m < limit->m.min || limit->m.max < clock->m)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
|
|
|
|
return false;
|
|
|
|
/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
|
|
|
|
* connector, etc., rather than just a single range.
|
|
|
|
*/
|
|
|
|
if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
i9xx_select_p2_div(const struct intel_limit *limit,
|
|
|
|
const struct intel_crtc_state *crtc_state,
|
|
|
|
int target)
|
|
|
|
{
|
2025-03-21 12:52:51 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
|
|
|
/*
|
|
|
|
* For LVDS just rely on its current settings for dual-channel.
|
|
|
|
* We haven't figured out how to reliably set up different
|
|
|
|
* single/dual channel state, if we even can.
|
|
|
|
*/
|
2025-03-21 12:52:51 +02:00
|
|
|
if (intel_is_dual_link_lvds(display))
|
2021-01-14 13:13:46 +02:00
|
|
|
return limit->p2.p2_fast;
|
|
|
|
else
|
|
|
|
return limit->p2.p2_slow;
|
|
|
|
} else {
|
|
|
|
if (target < limit->p2.dot_limit)
|
|
|
|
return limit->p2.p2_slow;
|
|
|
|
else
|
|
|
|
return limit->p2.p2_fast;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
2022-03-08 01:39:34 +02:00
|
|
|
* refclk, or FALSE.
|
2021-01-14 13:13:46 +02:00
|
|
|
*
|
|
|
|
* Target and reference clocks are specified in kHz.
|
|
|
|
*
|
|
|
|
* If match_clock is provided, then best_clock P divider must match the P
|
|
|
|
* divider from @match_clock used for LVDS downclocking.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
i9xx_find_best_dpll(const struct intel_limit *limit,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:21 +03:00
|
|
|
int target, int refclk,
|
|
|
|
const struct dpll *match_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll clock;
|
|
|
|
int err = target;
|
|
|
|
|
|
|
|
memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
|
|
|
|
clock.p2 = i9xx_select_p2_div(limit, crtc_state, target);
|
|
|
|
|
|
|
|
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
|
|
|
|
clock.m1++) {
|
|
|
|
for (clock.m2 = limit->m2.min;
|
|
|
|
clock.m2 <= limit->m2.max; clock.m2++) {
|
|
|
|
if (clock.m2 >= clock.m1)
|
|
|
|
break;
|
|
|
|
for (clock.n = limit->n.min;
|
|
|
|
clock.n <= limit->n.max; clock.n++) {
|
|
|
|
for (clock.p1 = limit->p1.min;
|
|
|
|
clock.p1 <= limit->p1.max; clock.p1++) {
|
|
|
|
int this_err;
|
|
|
|
|
|
|
|
i9xx_calc_dpll_params(refclk, &clock);
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!intel_pll_is_valid(display,
|
2021-01-14 13:13:46 +02:00
|
|
|
limit,
|
|
|
|
&clock))
|
|
|
|
continue;
|
|
|
|
if (match_clock &&
|
|
|
|
clock.p != match_clock->p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this_err = abs(clock.dot - target);
|
|
|
|
if (this_err < err) {
|
|
|
|
*best_clock = clock;
|
|
|
|
err = this_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (err != target);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
2022-03-08 01:39:34 +02:00
|
|
|
* refclk, or FALSE.
|
2021-01-14 13:13:46 +02:00
|
|
|
*
|
|
|
|
* Target and reference clocks are specified in kHz.
|
|
|
|
*
|
|
|
|
* If match_clock is provided, then best_clock P divider must match the P
|
|
|
|
* divider from @match_clock used for LVDS downclocking.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
pnv_find_best_dpll(const struct intel_limit *limit,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:21 +03:00
|
|
|
int target, int refclk,
|
|
|
|
const struct dpll *match_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll clock;
|
|
|
|
int err = target;
|
|
|
|
|
|
|
|
memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
|
|
|
|
clock.p2 = i9xx_select_p2_div(limit, crtc_state, target);
|
|
|
|
|
|
|
|
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
|
|
|
|
clock.m1++) {
|
|
|
|
for (clock.m2 = limit->m2.min;
|
|
|
|
clock.m2 <= limit->m2.max; clock.m2++) {
|
|
|
|
for (clock.n = limit->n.min;
|
|
|
|
clock.n <= limit->n.max; clock.n++) {
|
|
|
|
for (clock.p1 = limit->p1.min;
|
|
|
|
clock.p1 <= limit->p1.max; clock.p1++) {
|
|
|
|
int this_err;
|
|
|
|
|
|
|
|
pnv_calc_dpll_params(refclk, &clock);
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!intel_pll_is_valid(display,
|
2021-01-14 13:13:46 +02:00
|
|
|
limit,
|
|
|
|
&clock))
|
|
|
|
continue;
|
|
|
|
if (match_clock &&
|
|
|
|
clock.p != match_clock->p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this_err = abs(clock.dot - target);
|
|
|
|
if (this_err < err) {
|
|
|
|
*best_clock = clock;
|
|
|
|
err = this_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (err != target);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
2022-03-08 01:39:34 +02:00
|
|
|
* refclk, or FALSE.
|
2021-01-14 13:13:46 +02:00
|
|
|
*
|
|
|
|
* Target and reference clocks are specified in kHz.
|
|
|
|
*
|
|
|
|
* If match_clock is provided, then best_clock P divider must match the P
|
|
|
|
* divider from @match_clock used for LVDS downclocking.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
g4x_find_best_dpll(const struct intel_limit *limit,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:21 +03:00
|
|
|
int target, int refclk,
|
|
|
|
const struct dpll *match_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll clock;
|
|
|
|
int max_n;
|
|
|
|
bool found = false;
|
|
|
|
/* approximately equals target * 0.00585 */
|
|
|
|
int err_most = (target >> 8) + (target >> 9);
|
|
|
|
|
|
|
|
memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
|
|
|
|
clock.p2 = i9xx_select_p2_div(limit, crtc_state, target);
|
|
|
|
|
|
|
|
max_n = limit->n.max;
|
|
|
|
/* based on hardware requirement, prefer smaller n to precision */
|
|
|
|
for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
|
2024-09-13 14:17:27 +08:00
|
|
|
/* based on hardware requirement, prefer larger m1,m2 */
|
2021-01-14 13:13:46 +02:00
|
|
|
for (clock.m1 = limit->m1.max;
|
|
|
|
clock.m1 >= limit->m1.min; clock.m1--) {
|
|
|
|
for (clock.m2 = limit->m2.max;
|
|
|
|
clock.m2 >= limit->m2.min; clock.m2--) {
|
|
|
|
for (clock.p1 = limit->p1.max;
|
|
|
|
clock.p1 >= limit->p1.min; clock.p1--) {
|
|
|
|
int this_err;
|
|
|
|
|
|
|
|
i9xx_calc_dpll_params(refclk, &clock);
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!intel_pll_is_valid(display,
|
2021-01-14 13:13:46 +02:00
|
|
|
limit,
|
|
|
|
&clock))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this_err = abs(clock.dot - target);
|
|
|
|
if (this_err < err_most) {
|
|
|
|
*best_clock = clock;
|
|
|
|
err_most = this_err;
|
|
|
|
max_n = clock.n;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the calculated PLL configuration is more optimal compared to the
|
|
|
|
* best configuration and error found so far. Return the calculated error.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
static bool vlv_PLL_is_optimal(struct intel_display *display, int target_freq,
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct dpll *calculated_clock,
|
|
|
|
const struct dpll *best_clock,
|
|
|
|
unsigned int best_error_ppm,
|
|
|
|
unsigned int *error_ppm)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* For CHV ignore the error and consider only the P value.
|
|
|
|
* Prefer a bigger P value based on HW requirements.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.cherryview) {
|
2021-01-14 13:13:46 +02:00
|
|
|
*error_ppm = 0;
|
|
|
|
|
|
|
|
return calculated_clock->p > best_clock->p;
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (drm_WARN_ON_ONCE(display->drm, !target_freq))
|
2021-01-14 13:13:46 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
*error_ppm = div_u64(1000000ULL *
|
|
|
|
abs(target_freq - calculated_clock->dot),
|
|
|
|
target_freq);
|
|
|
|
/*
|
|
|
|
* Prefer a better P value over a better (smaller) error if the error
|
|
|
|
* is small. Ensure this preference for future configurations too by
|
|
|
|
* setting the error to 0.
|
|
|
|
*/
|
|
|
|
if (*error_ppm < 100 && calculated_clock->p > best_clock->p) {
|
|
|
|
*error_ppm = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *error_ppm + 10 < best_error_ppm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
2022-03-08 01:39:34 +02:00
|
|
|
* refclk, or FALSE.
|
2021-01-14 13:13:46 +02:00
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
vlv_find_best_dpll(const struct intel_limit *limit,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:21 +03:00
|
|
|
int target, int refclk,
|
|
|
|
const struct dpll *match_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll clock;
|
|
|
|
unsigned int bestppm = 1000000;
|
|
|
|
/* min update 19.2 MHz */
|
|
|
|
int max_n = min(limit->n.max, refclk / 19200);
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
|
|
|
|
/* based on hardware requirement, prefer smaller n to precision */
|
|
|
|
for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
|
|
|
|
for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
|
|
|
|
for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow;
|
|
|
|
clock.p2 -= clock.p2 > 10 ? 2 : 1) {
|
2022-03-09 23:43:01 +02:00
|
|
|
clock.p = clock.p1 * clock.p2 * 5;
|
2021-01-14 13:13:46 +02:00
|
|
|
/* based on hardware requirement, prefer bigger m1,m2 values */
|
|
|
|
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
|
|
|
|
unsigned int ppm;
|
|
|
|
|
|
|
|
clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n,
|
|
|
|
refclk * clock.m1);
|
|
|
|
|
|
|
|
vlv_calc_dpll_params(refclk, &clock);
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!intel_pll_is_valid(display,
|
2021-01-14 13:13:46 +02:00
|
|
|
limit,
|
|
|
|
&clock))
|
|
|
|
continue;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!vlv_PLL_is_optimal(display, target,
|
2021-01-14 13:13:46 +02:00
|
|
|
&clock,
|
|
|
|
best_clock,
|
|
|
|
bestppm, &ppm))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*best_clock = clock;
|
|
|
|
bestppm = ppm;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
2022-03-08 01:39:34 +02:00
|
|
|
* refclk, or FALSE.
|
2021-01-14 13:13:46 +02:00
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
chv_find_best_dpll(const struct intel_limit *limit,
|
|
|
|
struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:21 +03:00
|
|
|
int target, int refclk,
|
|
|
|
const struct dpll *match_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
unsigned int best_error_ppm;
|
|
|
|
struct dpll clock;
|
|
|
|
u64 m2;
|
|
|
|
int found = false;
|
|
|
|
|
|
|
|
memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
best_error_ppm = 1000000;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Based on hardware doc, the n always set to 1, and m1 always
|
|
|
|
* set to 2. If requires to support 200Mhz refclk, we need to
|
|
|
|
* revisit this because n may not 1 anymore.
|
|
|
|
*/
|
|
|
|
clock.n = 1;
|
|
|
|
clock.m1 = 2;
|
|
|
|
|
|
|
|
for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
|
|
|
|
for (clock.p2 = limit->p2.p2_fast;
|
|
|
|
clock.p2 >= limit->p2.p2_slow;
|
|
|
|
clock.p2 -= clock.p2 > 10 ? 2 : 1) {
|
|
|
|
unsigned int error_ppm;
|
|
|
|
|
2022-03-09 23:43:01 +02:00
|
|
|
clock.p = clock.p1 * clock.p2 * 5;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
m2 = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(target, clock.p * clock.n) << 22,
|
|
|
|
refclk * clock.m1);
|
|
|
|
|
|
|
|
if (m2 > INT_MAX/clock.m1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
clock.m2 = m2;
|
|
|
|
|
|
|
|
chv_calc_dpll_params(refclk, &clock);
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!intel_pll_is_valid(display, limit, &clock))
|
2021-01-14 13:13:46 +02:00
|
|
|
continue;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (!vlv_PLL_is_optimal(display, target, &clock, best_clock,
|
2021-01-14 13:13:46 +02:00
|
|
|
best_error_ppm, &error_ppm))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*best_clock = clock;
|
|
|
|
best_error_ppm = error_ppm;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state,
|
|
|
|
struct dpll *best_clock)
|
|
|
|
{
|
|
|
|
const struct intel_limit *limit = &intel_limits_bxt;
|
2022-03-25 14:31:56 +02:00
|
|
|
int refclk = 100000;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
return chv_find_best_dpll(limit, crtc_state,
|
|
|
|
crtc_state->port_clock, refclk,
|
|
|
|
NULL, best_clock);
|
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:21 +03:00
|
|
|
u32 i9xx_dpll_compute_fp(const struct dpll *dpll)
|
|
|
|
{
|
|
|
|
return dpll->n << 16 | dpll->m1 << 8 | dpll->m2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 pnv_dpll_compute_fp(const struct dpll *dpll)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
|
|
|
return (1 << dpll->n) << 16 | dpll->m2;
|
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:54 +03:00
|
|
|
static u32 i965_dpll_md(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
|
|
|
return (crtc_state->pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
|
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
static u32 i9xx_dpll(const struct intel_crtc_state *crtc_state,
|
|
|
|
const struct dpll *clock,
|
|
|
|
const struct dpll *reduced_clock)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
u32 dpll;
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS))
|
|
|
|
dpll |= DPLLB_MODE_LVDS;
|
|
|
|
else
|
|
|
|
dpll |= DPLLB_MODE_DAC_SERIAL;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.i945g || display->platform.i945gm ||
|
|
|
|
display->platform.g33 || display->platform.pineview) {
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= (crtc_state->pixel_multiplier - 1)
|
|
|
|
<< SDVO_MULTIPLIER_SHIFT_HIRES;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) ||
|
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
|
|
|
|
dpll |= DPLL_SDVO_HIGH_SPEED;
|
|
|
|
|
|
|
|
if (intel_crtc_has_dp_encoder(crtc_state))
|
|
|
|
dpll |= DPLL_SDVO_HIGH_SPEED;
|
|
|
|
|
|
|
|
/* compute bitmask from p1 value */
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.g4x) {
|
2021-07-15 12:35:25 +03:00
|
|
|
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
|
|
|
dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
|
2025-03-25 14:36:33 +02:00
|
|
|
} else if (display->platform.pineview) {
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW;
|
2021-07-15 12:35:25 +03:00
|
|
|
WARN_ON(reduced_clock->p1 != clock->p1);
|
|
|
|
} else {
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
2021-07-15 12:35:25 +03:00
|
|
|
WARN_ON(reduced_clock->p1 != clock->p1);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
2021-07-15 12:35:25 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
switch (clock->p2) {
|
|
|
|
case 5:
|
|
|
|
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
|
|
|
|
break;
|
|
|
|
}
|
2021-07-15 12:35:25 +03:00
|
|
|
WARN_ON(reduced_clock->p2 != clock->p2);
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) >= 4)
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
|
|
|
|
|
|
|
|
if (crtc_state->sdvo_tv_clock)
|
|
|
|
dpll |= PLL_REF_INPUT_TVCLKINBC;
|
|
|
|
else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) &&
|
2024-10-16 17:31:32 +03:00
|
|
|
intel_panel_use_ssc(display))
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
|
|
|
|
else
|
|
|
|
dpll |= PLL_REF_INPUT_DREFCLK;
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
return dpll;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
static void i9xx_compute_dpll(struct intel_crtc_state *crtc_state,
|
2021-07-15 12:35:25 +03:00
|
|
|
const struct dpll *clock,
|
2021-07-15 12:35:21 +03:00
|
|
|
const struct dpll *reduced_clock)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.pineview) {
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->fp0 = pnv_dpll_compute_fp(clock);
|
|
|
|
hw_state->fp1 = pnv_dpll_compute_fp(reduced_clock);
|
2024-04-12 21:26:56 +03:00
|
|
|
} else {
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->fp0 = i9xx_dpll_compute_fp(clock);
|
|
|
|
hw_state->fp1 = i9xx_dpll_compute_fp(reduced_clock);
|
2024-04-12 21:26:56 +03:00
|
|
|
}
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->dpll = i9xx_dpll(crtc_state, clock, reduced_clock);
|
2024-04-12 21:26:55 +03:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) >= 4)
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->dpll_md = i965_dpll_md(crtc_state);
|
2024-04-12 21:26:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static u32 i8xx_dpll(const struct intel_crtc_state *crtc_state,
|
|
|
|
const struct dpll *clock,
|
|
|
|
const struct dpll *reduced_clock)
|
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:26:55 +03:00
|
|
|
u32 dpll;
|
|
|
|
|
|
|
|
dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
|
|
|
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
|
|
|
} else {
|
|
|
|
if (clock->p1 == 2)
|
|
|
|
dpll |= PLL_P1_DIVIDE_BY_TWO;
|
|
|
|
else
|
|
|
|
dpll |= (clock->p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
|
|
|
if (clock->p2 == 4)
|
|
|
|
dpll |= PLL_P2_DIVIDE_BY_4;
|
|
|
|
}
|
2021-07-15 12:35:25 +03:00
|
|
|
WARN_ON(reduced_clock->p1 != clock->p1);
|
|
|
|
WARN_ON(reduced_clock->p2 != clock->p2);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Bspec:
|
|
|
|
* "[Almador Errata}: For the correct operation of the muxed DVO pins
|
|
|
|
* (GDEVSELB/I2Cdata, GIRDBY/I2CClk) and (GFRAMEB/DVI_Data,
|
|
|
|
* GTRDYB/DVI_Clk): Bit 31 (DPLL VCO Enable) and Bit 30 (2X Clock
|
|
|
|
* Enable) must be set to “1” in both the DPLL A Control Register
|
|
|
|
* (06014h-06017h) and DPLL B Control Register (06018h-0601Bh)."
|
|
|
|
*
|
|
|
|
* For simplicity We simply keep both bits always enabled in
|
|
|
|
* both DPLLS. The spec says we should disable the DVO 2X clock
|
|
|
|
* when not needed, but this seems to work fine in practice.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.i830 ||
|
2021-01-14 13:13:46 +02:00
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO))
|
|
|
|
dpll |= DPLL_DVO_2X_MODE;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) &&
|
2024-10-16 17:31:32 +03:00
|
|
|
intel_panel_use_ssc(display))
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
|
|
|
|
else
|
|
|
|
dpll |= PLL_REF_INPUT_DREFCLK;
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
return dpll;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i8xx_compute_dpll(struct intel_crtc_state *crtc_state,
|
|
|
|
const struct dpll *clock,
|
|
|
|
const struct dpll *reduced_clock)
|
|
|
|
{
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-12 21:26:55 +03:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->fp0 = i9xx_dpll_compute_fp(clock);
|
|
|
|
hw_state->fp1 = i9xx_dpll_compute_fp(reduced_clock);
|
|
|
|
|
|
|
|
hw_state->dpll = i8xx_dpll(crtc_state, clock, reduced_clock);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int hsw_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2022-03-25 14:32:01 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-05-03 21:22:17 +03:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
struct intel_encoder *encoder =
|
|
|
|
intel_get_crtc_new_encoder(state, crtc_state);
|
2022-09-07 12:10:47 +03:00
|
|
|
int ret;
|
2022-05-03 21:22:17 +03:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) < 11 &&
|
2022-05-03 21:22:17 +03:00
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
|
|
|
return 0;
|
|
|
|
|
2025-05-15 12:48:00 +05:30
|
|
|
ret = intel_dpll_compute(state, crtc, encoder);
|
2022-09-07 12:10:47 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* FIXME this is a mess */
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* CRT dotclock is determined via other means */
|
|
|
|
if (!crtc_state->has_pch_encoder)
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
|
|
|
return 0;
|
2022-03-25 14:32:01 +02:00
|
|
|
}
|
|
|
|
|
2025-05-15 12:47:55 +05:30
|
|
|
static int hsw_crtc_get_dpll(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-07-23 10:42:32 -07:00
|
|
|
struct intel_encoder *encoder =
|
|
|
|
intel_get_crtc_new_encoder(state, crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) < 11 &&
|
2021-09-13 17:44:28 +03:00
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
|
|
|
return 0;
|
|
|
|
|
2025-05-15 12:47:59 +05:30
|
|
|
return intel_dpll_reserve(state, crtc, encoder);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 14:32:00 +02:00
|
|
|
static int dg2_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
struct intel_encoder *encoder =
|
|
|
|
intel_get_crtc_new_encoder(state, crtc_state);
|
2022-09-07 12:10:47 +03:00
|
|
|
int ret;
|
2022-03-25 14:32:00 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
ret = intel_mpllb_calc_state(crtc_state, encoder);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
|
|
|
return 0;
|
2022-03-25 14:32:00 +02:00
|
|
|
}
|
|
|
|
|
drm/i915/mtl: Add Support for C10 PHY message bus and pll programming
XELPDP has C10 and C20 phys from Synopsys to drive displays. Each phy
has a dedicated PIPE 5.2 Message bus for configuration. This message
bus is used to configure the phy internal registers.
XELPDP has C10 phys to drive output to the EDP and the native output
from the display engine. Add structures, programming hardware state
readout logic. Port clock calculations are similar to DG2. Use the DG2
formulae to calculate the port clock but use the relevant pll signals.
Note: PHY lane 0 is always used for PLL programming.
Add sequences for C10 phy enable/disable phy lane reset,
powerdown change sequence and phy lane programming.
Bspec: 64539, 64568, 64599, 65100, 65101, 65450, 65451, 67610, 67636
v2: Squash patches related to C10 phy message bus and pll
programming support (Jani)
Move register definitions to a new file i.e. intel_cx0_reg_defs.h (Jani)
Move macro definitions (Jani)
DP rates as separate patch (Jani)
Spin out xelpdp register definitions into a separate file (Jani)
Replace macro to select registers based on phy lane with
function calls (Jani)
Fix styling issues (Jani)
Call XELPDP_PORT_P2M_MSGBUS_STATUS() with port instead of phy (Lucas)
v3: Move clear request flag into try-loop
v4: On PHY idle change drm_err_once() as drm_dbg_kms() (Jani)
use __intel_de_wait_for_register() instead of __intel_wait_for_register
and uncomment intel_uncore.h (Jani)
Add DP-alt support for PHY lane programming (Khaled)
v4: Add tx and cmn on c10mpllb_state (Imre)
Add missing waits for pending transactions between two message bus
writes (Imre)
General cleanups and simplifications (Imre)
v5: Few nit cleanups from rev4 (imre)
s/dev_priv/i915/ , s/c10mpllb/c10pll/ (RK)
Rebase
v6: Move the mtl code from intel_c10pll_calc_port_clock to mtl function
Fix typo in comment for REG_FIELD_PREP8 definition(Imre)
Cc: Mika Kahola <mika.kahola@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Gustavo Sousa <gustavo.sousa@intel.com>
Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com>
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com> (v4)
Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-4-radhakrishna.sripada@intel.com
2023-04-13 14:24:37 -07:00
|
|
|
static int mtl_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
struct intel_encoder *encoder =
|
|
|
|
intel_get_crtc_new_encoder(state, crtc_state);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = intel_cx0pll_calc_state(crtc_state, encoder);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2025-05-15 12:48:00 +05:30
|
|
|
/* TODO: Do the readback via intel_dpll_compute() */
|
2024-04-12 21:27:03 +03:00
|
|
|
crtc_state->port_clock = intel_cx0pll_calc_port_clock(encoder, &crtc_state->dpll_hw_state.cx0pll);
|
drm/i915/mtl: Add Support for C10 PHY message bus and pll programming
XELPDP has C10 and C20 phys from Synopsys to drive displays. Each phy
has a dedicated PIPE 5.2 Message bus for configuration. This message
bus is used to configure the phy internal registers.
XELPDP has C10 phys to drive output to the EDP and the native output
from the display engine. Add structures, programming hardware state
readout logic. Port clock calculations are similar to DG2. Use the DG2
formulae to calculate the port clock but use the relevant pll signals.
Note: PHY lane 0 is always used for PLL programming.
Add sequences for C10 phy enable/disable phy lane reset,
powerdown change sequence and phy lane programming.
Bspec: 64539, 64568, 64599, 65100, 65101, 65450, 65451, 67610, 67636
v2: Squash patches related to C10 phy message bus and pll
programming support (Jani)
Move register definitions to a new file i.e. intel_cx0_reg_defs.h (Jani)
Move macro definitions (Jani)
DP rates as separate patch (Jani)
Spin out xelpdp register definitions into a separate file (Jani)
Replace macro to select registers based on phy lane with
function calls (Jani)
Fix styling issues (Jani)
Call XELPDP_PORT_P2M_MSGBUS_STATUS() with port instead of phy (Lucas)
v3: Move clear request flag into try-loop
v4: On PHY idle change drm_err_once() as drm_dbg_kms() (Jani)
use __intel_de_wait_for_register() instead of __intel_wait_for_register
and uncomment intel_uncore.h (Jani)
Add DP-alt support for PHY lane programming (Khaled)
v4: Add tx and cmn on c10mpllb_state (Imre)
Add missing waits for pending transactions between two message bus
writes (Imre)
General cleanups and simplifications (Imre)
v5: Few nit cleanups from rev4 (imre)
s/dev_priv/i915/ , s/c10mpllb/c10pll/ (RK)
Rebase
v6: Move the mtl code from intel_c10pll_calc_port_clock to mtl function
Fix typo in comment for REG_FIELD_PREP8 definition(Imre)
Cc: Mika Kahola <mika.kahola@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Gustavo Sousa <gustavo.sousa@intel.com>
Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com>
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com> (v4)
Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-4-radhakrishna.sripada@intel.com
2023-04-13 14:24:37 -07:00
|
|
|
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:50 +03:00
|
|
|
static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2024-04-12 21:26:50 +03:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) &&
|
2025-03-25 14:36:33 +02:00
|
|
|
((intel_panel_use_ssc(display) && display->vbt.lvds_ssc_freq == 100000) ||
|
2025-04-17 12:10:36 +03:00
|
|
|
(HAS_PCH_IBX(display) && intel_is_dual_link_lvds(display))))
|
2024-04-12 21:26:50 +03:00
|
|
|
return 25;
|
|
|
|
|
|
|
|
if (crtc_state->sdvo_tv_clock)
|
|
|
|
return 20;
|
|
|
|
|
|
|
|
return 21;
|
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:21 +03:00
|
|
|
static bool ilk_needs_fb_cb_tune(const struct dpll *dpll, int factor)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2021-07-15 12:35:27 +03:00
|
|
|
return dpll->m < factor * dpll->n;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:51 +03:00
|
|
|
static u32 ilk_dpll_compute_fp(const struct dpll *clock, int factor)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-04-12 21:26:51 +03:00
|
|
|
u32 fp;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2021-07-15 12:35:21 +03:00
|
|
|
fp = i9xx_dpll_compute_fp(clock);
|
|
|
|
if (ilk_needs_fb_cb_tune(clock, factor))
|
2021-01-14 13:13:46 +02:00
|
|
|
fp |= FP_CB_TUNE;
|
|
|
|
|
2024-04-12 21:26:51 +03:00
|
|
|
return fp;
|
|
|
|
}
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
static u32 ilk_dpll(const struct intel_crtc_state *crtc_state,
|
|
|
|
const struct dpll *clock,
|
|
|
|
const struct dpll *reduced_clock)
|
2021-07-15 12:35:20 +03:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:20 +03:00
|
|
|
u32 dpll;
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll = DPLL_VCO_ENABLE;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS))
|
|
|
|
dpll |= DPLLB_MODE_LVDS;
|
|
|
|
else
|
|
|
|
dpll |= DPLLB_MODE_DAC_SERIAL;
|
|
|
|
|
|
|
|
dpll |= (crtc_state->pixel_multiplier - 1)
|
|
|
|
<< PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO) ||
|
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
|
|
|
|
dpll |= DPLL_SDVO_HIGH_SPEED;
|
|
|
|
|
|
|
|
if (intel_crtc_has_dp_encoder(crtc_state))
|
|
|
|
dpll |= DPLL_SDVO_HIGH_SPEED;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The high speed IO clock is only really required for
|
|
|
|
* SDVO/HDMI/DP, but we also enable it for CRT to make it
|
|
|
|
* possible to share the DPLL between CRT and HDMI. Enabling
|
|
|
|
* the clock needlessly does no real harm, except use up a
|
|
|
|
* bit of power potentially.
|
|
|
|
*
|
|
|
|
* We'll limit this to IVB with 3 pipes, since it has only two
|
|
|
|
* DPLLs and so DPLL sharing is the only way to get three pipes
|
|
|
|
* driving PCH ports at the same time. On SNB we could do this,
|
|
|
|
* and potentially avoid enabling the second DPLL, but it's not
|
|
|
|
* clear if it''s a win or loss power wise. No point in doing
|
|
|
|
* this on ILK at all since it has a fixed DPLL<->pipe mapping.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
if (INTEL_NUM_PIPES(display) == 3 &&
|
2021-01-14 13:13:46 +02:00
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG))
|
|
|
|
dpll |= DPLL_SDVO_HIGH_SPEED;
|
|
|
|
|
|
|
|
/* compute bitmask from p1 value */
|
2021-07-15 12:35:21 +03:00
|
|
|
dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
|
2021-01-14 13:13:46 +02:00
|
|
|
/* also FPA1 */
|
2021-07-15 12:35:25 +03:00
|
|
|
dpll |= (1 << (reduced_clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2021-07-15 12:35:21 +03:00
|
|
|
switch (clock->p2) {
|
2021-01-14 13:13:46 +02:00
|
|
|
case 5:
|
|
|
|
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
|
|
|
|
break;
|
|
|
|
}
|
2021-07-15 12:35:25 +03:00
|
|
|
WARN_ON(reduced_clock->p2 != clock->p2);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS) &&
|
2024-10-16 17:31:32 +03:00
|
|
|
intel_panel_use_ssc(display))
|
2021-01-14 13:13:46 +02:00
|
|
|
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
|
|
|
|
else
|
|
|
|
dpll |= PLL_REF_INPUT_DREFCLK;
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
return dpll;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ilk_compute_dpll(struct intel_crtc_state *crtc_state,
|
|
|
|
const struct dpll *clock,
|
|
|
|
const struct dpll *reduced_clock)
|
|
|
|
{
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-12 21:26:56 +03:00
|
|
|
int factor = ilk_fb_cb_factor(crtc_state);
|
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->fp0 = ilk_dpll_compute_fp(clock, factor);
|
|
|
|
hw_state->fp1 = ilk_dpll_compute_fp(reduced_clock, factor);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->dpll = ilk_dpll(crtc_state, clock, reduced_clock);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int ilk_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit;
|
|
|
|
int refclk = 120000;
|
2022-09-07 12:10:47 +03:00
|
|
|
int ret;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
/* CPU eDP is the only output that doesn't need a PCH PLL of its own. */
|
|
|
|
if (!crtc_state->has_pch_encoder)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
2024-10-16 17:31:32 +03:00
|
|
|
if (intel_panel_use_ssc(display)) {
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_dbg_kms(display->drm,
|
2021-01-14 13:13:46 +02:00
|
|
|
"using SSC reference clock of %d kHz\n",
|
2025-03-25 14:36:33 +02:00
|
|
|
display->vbt.lvds_ssc_freq);
|
|
|
|
refclk = display->vbt.lvds_ssc_freq;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2025-03-21 12:52:51 +02:00
|
|
|
if (intel_is_dual_link_lvds(display)) {
|
2021-01-14 13:13:46 +02:00
|
|
|
if (refclk == 100000)
|
|
|
|
limit = &ilk_limits_dual_lvds_100m;
|
|
|
|
else
|
|
|
|
limit = &ilk_limits_dual_lvds;
|
|
|
|
} else {
|
|
|
|
if (refclk == 100000)
|
|
|
|
limit = &ilk_limits_single_lvds_100m;
|
|
|
|
else
|
|
|
|
limit = &ilk_limits_single_lvds;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
limit = &ilk_limits_dac;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
i9xx_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:25 +03:00
|
|
|
ilk_compute_dpll(crtc_state, &crtc_state->dpll,
|
|
|
|
&crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2025-05-15 12:48:00 +05:30
|
|
|
ret = intel_dpll_compute(state, crtc, NULL);
|
2022-09-07 12:10:47 +03:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
|
|
|
return ret;
|
2022-03-25 14:32:01 +02:00
|
|
|
}
|
|
|
|
|
2025-05-15 12:47:55 +05:30
|
|
|
static int ilk_crtc_get_dpll(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2022-03-25 14:32:01 +02:00
|
|
|
{
|
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
|
|
|
|
/* CPU eDP is the only output that doesn't need a PCH PLL of its own. */
|
|
|
|
if (!crtc_state->has_pch_encoder)
|
|
|
|
return 0;
|
|
|
|
|
2025-05-15 12:47:59 +05:30
|
|
|
return intel_dpll_reserve(state, crtc, NULL);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
static u32 vlv_dpll(const struct intel_crtc_state *crtc_state)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2021-07-15 12:35:23 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:26:55 +03:00
|
|
|
u32 dpll;
|
2021-07-15 12:35:22 +03:00
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll = DPLL_INTEGRATED_REF_CLK_VLV |
|
2021-01-14 13:13:46 +02:00
|
|
|
DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
|
2024-04-12 21:26:55 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
if (crtc->pipe != PIPE_A)
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
/* DPLL not used with DSI, but still need the rest set up */
|
2021-07-15 12:35:23 +03:00
|
|
|
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll |= DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV;
|
|
|
|
|
|
|
|
return dpll;
|
|
|
|
}
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
void vlv_compute_dpll(struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-12 21:27:00 +03:00
|
|
|
|
|
|
|
hw_state->dpll = vlv_dpll(crtc_state);
|
|
|
|
hw_state->dpll_md = i965_dpll_md(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
static u32 chv_dpll(const struct intel_crtc_state *crtc_state)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2021-07-15 12:35:23 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:26:55 +03:00
|
|
|
u32 dpll;
|
2021-07-15 12:35:22 +03:00
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll = DPLL_SSC_REF_CLK_CHV |
|
2021-01-14 13:13:46 +02:00
|
|
|
DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
|
2024-04-12 21:26:55 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
if (crtc->pipe != PIPE_A)
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
/* DPLL not used with DSI, but still need the rest set up */
|
2021-07-15 12:35:23 +03:00
|
|
|
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
2024-04-12 21:26:55 +03:00
|
|
|
dpll |= DPLL_VCO_ENABLE;
|
|
|
|
|
|
|
|
return dpll;
|
|
|
|
}
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2024-04-12 21:26:55 +03:00
|
|
|
void chv_compute_dpll(struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2024-04-12 21:27:01 +03:00
|
|
|
struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-12 21:27:00 +03:00
|
|
|
|
|
|
|
hw_state->dpll = chv_dpll(crtc_state);
|
|
|
|
hw_state->dpll_md = i965_dpll_md(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int chv_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit = &intel_limits_chv;
|
2022-03-25 14:31:56 +02:00
|
|
|
int refclk = 100000;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!chv_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
chv_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:22 +03:00
|
|
|
chv_compute_dpll(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
/* FIXME this is a mess */
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int vlv_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit = &intel_limits_vlv;
|
2022-03-25 14:31:56 +02:00
|
|
|
int refclk = 100000;
|
2021-01-14 13:13:46 +02:00
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!vlv_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2023-07-05 23:21:14 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
2023-07-05 23:21:14 +03:00
|
|
|
|
|
|
|
vlv_calc_dpll_params(refclk, &crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2021-07-15 12:35:22 +03:00
|
|
|
vlv_compute_dpll(crtc_state);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
/* FIXME this is a mess */
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int g4x_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit;
|
|
|
|
int refclk = 96000;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
2024-10-16 17:31:32 +03:00
|
|
|
if (intel_panel_use_ssc(display)) {
|
2025-03-25 14:36:33 +02:00
|
|
|
refclk = display->vbt.lvds_ssc_freq;
|
|
|
|
drm_dbg_kms(display->drm,
|
2021-01-14 13:13:46 +02:00
|
|
|
"using SSC reference clock of %d kHz\n",
|
|
|
|
refclk);
|
|
|
|
}
|
|
|
|
|
2025-03-21 12:52:51 +02:00
|
|
|
if (intel_is_dual_link_lvds(display))
|
2021-01-14 13:13:46 +02:00
|
|
|
limit = &intel_limits_g4x_dual_channel_lvds;
|
|
|
|
else
|
|
|
|
limit = &intel_limits_g4x_single_channel_lvds;
|
|
|
|
} else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) ||
|
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) {
|
|
|
|
limit = &intel_limits_g4x_hdmi;
|
|
|
|
} else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_SDVO)) {
|
|
|
|
limit = &intel_limits_g4x_sdvo;
|
|
|
|
} else {
|
|
|
|
/* The option is for other outputs */
|
|
|
|
limit = &intel_limits_i9xx_sdvo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
i9xx_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:25 +03:00
|
|
|
i9xx_compute_dpll(crtc_state, &crtc_state->dpll,
|
|
|
|
&crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
2022-09-09 23:59:32 +03:00
|
|
|
/* FIXME this is a mess */
|
|
|
|
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_TVOUT))
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
2022-09-07 12:10:47 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int pnv_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit;
|
|
|
|
int refclk = 96000;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
2024-10-16 17:31:32 +03:00
|
|
|
if (intel_panel_use_ssc(display)) {
|
2025-03-25 14:36:33 +02:00
|
|
|
refclk = display->vbt.lvds_ssc_freq;
|
|
|
|
drm_dbg_kms(display->drm,
|
2021-01-14 13:13:46 +02:00
|
|
|
"using SSC reference clock of %d kHz\n",
|
|
|
|
refclk);
|
|
|
|
}
|
|
|
|
|
|
|
|
limit = &pnv_limits_lvds;
|
|
|
|
} else {
|
|
|
|
limit = &pnv_limits_sdvo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!pnv_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
pnv_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:25 +03:00
|
|
|
i9xx_compute_dpll(crtc_state, &crtc_state->dpll,
|
|
|
|
&crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int i9xx_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit;
|
|
|
|
int refclk = 96000;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
2024-10-16 17:31:32 +03:00
|
|
|
if (intel_panel_use_ssc(display)) {
|
2025-03-25 14:36:33 +02:00
|
|
|
refclk = display->vbt.lvds_ssc_freq;
|
|
|
|
drm_dbg_kms(display->drm,
|
2021-01-14 13:13:46 +02:00
|
|
|
"using SSC reference clock of %d kHz\n",
|
|
|
|
refclk);
|
|
|
|
}
|
|
|
|
|
|
|
|
limit = &intel_limits_i9xx_lvds;
|
|
|
|
} else {
|
|
|
|
limit = &intel_limits_i9xx_sdvo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
i9xx_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:25 +03:00
|
|
|
i9xx_compute_dpll(crtc_state, &crtc_state->dpll,
|
|
|
|
&crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
2022-09-09 23:59:32 +03:00
|
|
|
/* FIXME this is a mess */
|
|
|
|
if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_TVOUT))
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
2022-09-07 12:10:47 +03:00
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
static int i8xx_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2021-01-14 13:13:46 +02:00
|
|
|
{
|
2024-10-16 17:31:32 +03:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:56 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2021-01-14 13:13:46 +02:00
|
|
|
const struct intel_limit *limit;
|
|
|
|
int refclk = 48000;
|
|
|
|
|
|
|
|
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_LVDS)) {
|
2024-10-16 17:31:32 +03:00
|
|
|
if (intel_panel_use_ssc(display)) {
|
2025-03-25 14:36:33 +02:00
|
|
|
refclk = display->vbt.lvds_ssc_freq;
|
|
|
|
drm_dbg_kms(display->drm,
|
2021-01-14 13:13:46 +02:00
|
|
|
"using SSC reference clock of %d kHz\n",
|
|
|
|
refclk);
|
|
|
|
}
|
|
|
|
|
|
|
|
limit = &intel_limits_i8xx_lvds;
|
|
|
|
} else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DVO)) {
|
|
|
|
limit = &intel_limits_i8xx_dvo;
|
|
|
|
} else {
|
|
|
|
limit = &intel_limits_i8xx_dac;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!crtc_state->clock_set &&
|
|
|
|
!i9xx_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
|
2022-05-03 21:22:19 +03:00
|
|
|
refclk, NULL, &crtc_state->dpll))
|
2021-01-14 13:13:46 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-07-05 23:21:14 +03:00
|
|
|
i9xx_calc_dpll_params(refclk, &crtc_state->dpll);
|
|
|
|
|
2021-07-15 12:35:25 +03:00
|
|
|
i8xx_compute_dpll(crtc_state, &crtc_state->dpll,
|
|
|
|
&crtc_state->dpll);
|
2021-01-14 13:13:46 +02:00
|
|
|
|
2022-09-07 12:10:47 +03:00
|
|
|
crtc_state->port_clock = crtc_state->dpll.dot;
|
|
|
|
crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs mtl_dpll_funcs = {
|
drm/i915/mtl: Add Support for C10 PHY message bus and pll programming
XELPDP has C10 and C20 phys from Synopsys to drive displays. Each phy
has a dedicated PIPE 5.2 Message bus for configuration. This message
bus is used to configure the phy internal registers.
XELPDP has C10 phys to drive output to the EDP and the native output
from the display engine. Add structures, programming hardware state
readout logic. Port clock calculations are similar to DG2. Use the DG2
formulae to calculate the port clock but use the relevant pll signals.
Note: PHY lane 0 is always used for PLL programming.
Add sequences for C10 phy enable/disable phy lane reset,
powerdown change sequence and phy lane programming.
Bspec: 64539, 64568, 64599, 65100, 65101, 65450, 65451, 67610, 67636
v2: Squash patches related to C10 phy message bus and pll
programming support (Jani)
Move register definitions to a new file i.e. intel_cx0_reg_defs.h (Jani)
Move macro definitions (Jani)
DP rates as separate patch (Jani)
Spin out xelpdp register definitions into a separate file (Jani)
Replace macro to select registers based on phy lane with
function calls (Jani)
Fix styling issues (Jani)
Call XELPDP_PORT_P2M_MSGBUS_STATUS() with port instead of phy (Lucas)
v3: Move clear request flag into try-loop
v4: On PHY idle change drm_err_once() as drm_dbg_kms() (Jani)
use __intel_de_wait_for_register() instead of __intel_wait_for_register
and uncomment intel_uncore.h (Jani)
Add DP-alt support for PHY lane programming (Khaled)
v4: Add tx and cmn on c10mpllb_state (Imre)
Add missing waits for pending transactions between two message bus
writes (Imre)
General cleanups and simplifications (Imre)
v5: Few nit cleanups from rev4 (imre)
s/dev_priv/i915/ , s/c10mpllb/c10pll/ (RK)
Rebase
v6: Move the mtl code from intel_c10pll_calc_port_clock to mtl function
Fix typo in comment for REG_FIELD_PREP8 definition(Imre)
Cc: Mika Kahola <mika.kahola@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Uma Shankar <uma.shankar@intel.com>
Cc: Gustavo Sousa <gustavo.sousa@intel.com>
Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com>
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com> (v4)
Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-4-radhakrishna.sripada@intel.com
2023-04-13 14:24:37 -07:00
|
|
|
.crtc_compute_clock = mtl_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs dg2_dpll_funcs = {
|
2022-03-25 14:32:00 +02:00
|
|
|
.crtc_compute_clock = dg2_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs hsw_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = hsw_crtc_compute_clock,
|
2025-05-15 12:47:55 +05:30
|
|
|
.crtc_get_dpll = hsw_crtc_get_dpll,
|
2021-09-29 01:58:03 +03:00
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs ilk_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = ilk_crtc_compute_clock,
|
2025-05-15 12:47:55 +05:30
|
|
|
.crtc_get_dpll = ilk_crtc_get_dpll,
|
2021-09-29 01:58:03 +03:00
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs chv_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = chv_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs vlv_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = vlv_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs g4x_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = g4x_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs pnv_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = pnv_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs i9xx_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = i9xx_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2025-05-15 12:47:49 +05:30
|
|
|
static const struct intel_dpll_global_funcs i8xx_dpll_funcs = {
|
2021-09-29 01:58:03 +03:00
|
|
|
.crtc_compute_clock = i8xx_crtc_compute_clock,
|
|
|
|
};
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
int intel_dpll_crtc_compute_clock(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2022-02-03 16:02:31 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:31:57 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2022-05-03 21:22:19 +03:00
|
|
|
int ret;
|
2022-03-25 14:31:57 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_WARN_ON(display->drm, !intel_crtc_needs_modeset(crtc_state));
|
2022-03-25 14:31:57 +02:00
|
|
|
|
2022-03-25 14:31:58 +02:00
|
|
|
memset(&crtc_state->dpll_hw_state, 0,
|
|
|
|
sizeof(crtc_state->dpll_hw_state));
|
|
|
|
|
2022-03-25 14:31:59 +02:00
|
|
|
if (!crtc_state->hw.enable)
|
|
|
|
return 0;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
ret = display->funcs.dpll->crtc_compute_clock(state, crtc);
|
2022-05-03 21:22:19 +03:00
|
|
|
if (ret) {
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_dbg_kms(display->drm, "[CRTC:%d:%s] Couldn't calculate DPLL settings\n",
|
2022-05-03 21:22:19 +03:00
|
|
|
crtc->base.base.id, crtc->base.name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2022-02-03 16:02:31 +02:00
|
|
|
}
|
|
|
|
|
2025-05-15 12:47:55 +05:30
|
|
|
int intel_dpll_crtc_get_dpll(struct intel_atomic_state *state,
|
|
|
|
struct intel_crtc *crtc)
|
2022-03-25 14:32:01 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(state);
|
2022-03-25 14:32:01 +02:00
|
|
|
struct intel_crtc_state *crtc_state =
|
|
|
|
intel_atomic_get_new_crtc_state(state, crtc);
|
2022-05-03 21:22:19 +03:00
|
|
|
int ret;
|
2022-03-25 14:32:01 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_WARN_ON(display->drm, !intel_crtc_needs_modeset(crtc_state));
|
2025-05-15 12:47:53 +05:30
|
|
|
drm_WARN_ON(display->drm, !crtc_state->hw.enable && crtc_state->intel_dpll);
|
2022-03-25 14:32:01 +02:00
|
|
|
|
2025-05-15 12:47:53 +05:30
|
|
|
if (!crtc_state->hw.enable || crtc_state->intel_dpll)
|
2022-03-25 14:32:01 +02:00
|
|
|
return 0;
|
|
|
|
|
2025-05-15 12:47:55 +05:30
|
|
|
if (!display->funcs.dpll->crtc_get_dpll)
|
2022-03-25 14:32:01 +02:00
|
|
|
return 0;
|
|
|
|
|
2025-05-15 12:47:55 +05:30
|
|
|
ret = display->funcs.dpll->crtc_get_dpll(state, crtc);
|
2022-05-03 21:22:19 +03:00
|
|
|
if (ret) {
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_dbg_kms(display->drm, "[CRTC:%d:%s] Couldn't get a shared DPLL\n",
|
2022-05-03 21:22:19 +03:00
|
|
|
crtc->base.base.id, crtc->base.name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2022-03-25 14:32:01 +02:00
|
|
|
}
|
|
|
|
|
2021-01-14 13:13:46 +02:00
|
|
|
void
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_dpll_init_clock_hook(struct intel_display *display)
|
|
|
|
{
|
|
|
|
if (DISPLAY_VER(display) >= 14)
|
|
|
|
display->funcs.dpll = &mtl_dpll_funcs;
|
|
|
|
else if (display->platform.dg2)
|
|
|
|
display->funcs.dpll = &dg2_dpll_funcs;
|
|
|
|
else if (DISPLAY_VER(display) >= 9 || HAS_DDI(display))
|
|
|
|
display->funcs.dpll = &hsw_dpll_funcs;
|
2025-04-17 12:10:36 +03:00
|
|
|
else if (HAS_PCH_SPLIT(display))
|
2025-03-25 14:36:33 +02:00
|
|
|
display->funcs.dpll = &ilk_dpll_funcs;
|
|
|
|
else if (display->platform.cherryview)
|
|
|
|
display->funcs.dpll = &chv_dpll_funcs;
|
|
|
|
else if (display->platform.valleyview)
|
|
|
|
display->funcs.dpll = &vlv_dpll_funcs;
|
|
|
|
else if (display->platform.g4x)
|
|
|
|
display->funcs.dpll = &g4x_dpll_funcs;
|
|
|
|
else if (display->platform.pineview)
|
|
|
|
display->funcs.dpll = &pnv_dpll_funcs;
|
|
|
|
else if (DISPLAY_VER(display) != 2)
|
|
|
|
display->funcs.dpll = &i9xx_dpll_funcs;
|
2021-01-14 13:13:46 +02:00
|
|
|
else
|
2025-03-25 14:36:33 +02:00
|
|
|
display->funcs.dpll = &i8xx_dpll_funcs;
|
2021-01-14 13:13:46 +02:00
|
|
|
}
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
static bool i9xx_has_pps(struct intel_display *display)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.i830)
|
2021-02-05 16:48:40 +02:00
|
|
|
return false;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
return display->platform.pineview || display->platform.mobile;
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:22 +03:00
|
|
|
void i9xx_enable_pll(const struct intel_crtc_state *crtc_state)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2024-08-30 13:15:45 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:22 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2021-07-15 12:35:24 +03:00
|
|
|
enum pipe pipe = crtc->pipe;
|
2021-02-05 16:48:40 +02:00
|
|
|
int i;
|
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* PLL is protected by panel, make sure we can write it */
|
2025-03-25 14:36:33 +02:00
|
|
|
if (i9xx_has_pps(display))
|
2024-08-30 13:15:45 +03:00
|
|
|
assert_pps_unlocked(display, pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, FP0(pipe), hw_state->fp0);
|
|
|
|
intel_de_write(display, FP1(pipe), hw_state->fp1);
|
2021-07-15 12:35:28 +03:00
|
|
|
|
2021-02-05 16:48:40 +02:00
|
|
|
/*
|
|
|
|
* Apparently we need to have VGA mode enabled prior to changing
|
|
|
|
* the P1/P2 dividers. Otherwise the DPLL will keep using the old
|
|
|
|
* dividers, even though the register value does change.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe),
|
2024-06-04 18:25:19 +03:00
|
|
|
hw_state->dpll & ~DPLL_VGA_MODE_DIS);
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Wait for the clocks to stabilize. */
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-02-05 16:48:40 +02:00
|
|
|
udelay(150);
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (DISPLAY_VER(display) >= 4) {
|
|
|
|
intel_de_write(display, DPLL_MD(display, pipe),
|
2024-06-04 18:25:20 +03:00
|
|
|
hw_state->dpll_md);
|
2021-02-05 16:48:40 +02:00
|
|
|
} else {
|
|
|
|
/* The pixel multiplier can only be updated once the
|
|
|
|
* DPLL is enabled and the clocks are stable.
|
|
|
|
*
|
|
|
|
* So write it again.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We do this three times for luck */
|
|
|
|
for (i = 0; i < 3; i++) {
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
|
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-02-05 16:48:40 +02:00
|
|
|
udelay(150); /* wait for warmup */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
static void vlv_pllb_recal_opamp(struct intel_display *display,
|
2024-04-22 11:34:51 +03:00
|
|
|
enum dpio_phy phy, enum dpio_channel ch)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2024-04-22 11:34:49 +03:00
|
|
|
u32 tmp;
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PLLB opamp always calibrates to max value of 0x3f, force enable it
|
|
|
|
* and set it to a reasonable value instead.
|
|
|
|
*/
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_PLL_DW17(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= 0xffffff00;
|
|
|
|
tmp |= 0x00000030;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW17(ch), tmp);
|
2024-04-22 11:34:49 +03:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_REF_DW11);
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= 0x00ffffff;
|
|
|
|
tmp |= 0x8c000000;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_REF_DW11, tmp);
|
2024-04-22 11:34:49 +03:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_PLL_DW17(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= 0xffffff00;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW17(ch), tmp);
|
2024-04-22 11:34:49 +03:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_REF_DW11);
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= 0x00ffffff;
|
|
|
|
tmp |= 0xb0000000;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_REF_DW11, tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:26 +03:00
|
|
|
static void vlv_prepare_pll(const struct intel_crtc_state *crtc_state)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:23 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-22 11:34:48 +03:00
|
|
|
const struct dpll *clock = &crtc_state->dpll;
|
2024-04-22 11:34:51 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(crtc->pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
enum pipe pipe = crtc->pipe;
|
2024-04-22 11:34:49 +03:00
|
|
|
u32 tmp, coreclk;
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* See eDP HDMI DPIO driver vbios notes doc */
|
|
|
|
|
|
|
|
/* PLL B needs special handling */
|
|
|
|
if (pipe == PIPE_B)
|
2025-03-25 14:36:33 +02:00
|
|
|
vlv_pllb_recal_opamp(display, phy, ch);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Set up Tx target for periodic Rcomp update */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PCS_DW17_BCAST, 0x0100000f);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Disable target IRef on PLL */
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, VLV_PLL_DW16(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= 0x00ffffff;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW16(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Disable fast lock */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_CMN_DW0, 0x610);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Set idtafcrecal before PLL is enabled */
|
2024-04-22 11:34:56 +03:00
|
|
|
tmp = DPIO_M1_DIV(clock->m1) |
|
|
|
|
DPIO_M2_DIV(clock->m2) |
|
|
|
|
DPIO_P1_DIV(clock->p1) |
|
|
|
|
DPIO_P2_DIV(clock->p2) |
|
|
|
|
DPIO_N_DIV(clock->n) |
|
|
|
|
DPIO_K_DIV(1);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Post divider depends on pixel clock rate, DAC vs digital (and LVDS,
|
|
|
|
* but we don't support that).
|
|
|
|
* Note: don't use the DAC post divider as it seems unstable.
|
|
|
|
*/
|
2024-04-22 11:34:56 +03:00
|
|
|
tmp |= DPIO_S1_DIV(DPIO_S1_DIV_HDMIDP);
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW3(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp |= DPIO_ENABLE_CALIBRATION;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW3(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Set HBR and RBR LPF coefficients */
|
2021-07-15 12:35:23 +03:00
|
|
|
if (crtc_state->port_clock == 162000 ||
|
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG) ||
|
|
|
|
intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW18(ch), 0x009f0003);
|
2021-02-05 16:48:40 +02:00
|
|
|
else
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW18(ch), 0x00d0000f);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2021-07-15 12:35:23 +03:00
|
|
|
if (intel_crtc_has_dp_encoder(crtc_state)) {
|
2021-02-05 16:48:40 +02:00
|
|
|
/* Use SSC source */
|
|
|
|
if (pipe == PIPE_A)
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW5(ch), 0x0df40000);
|
2021-02-05 16:48:40 +02:00
|
|
|
else
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW5(ch), 0x0df70000);
|
2021-02-05 16:48:40 +02:00
|
|
|
} else { /* HDMI or VGA */
|
|
|
|
/* Use bend source */
|
|
|
|
if (pipe == PIPE_A)
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW5(ch), 0x0df70000);
|
2021-02-05 16:48:40 +02:00
|
|
|
else
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW5(ch), 0x0df40000);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
coreclk = vlv_dpio_read(display->drm, phy, VLV_PLL_DW7(ch));
|
2021-02-05 16:48:40 +02:00
|
|
|
coreclk = (coreclk & 0x0000ff00) | 0x01c00000;
|
2021-07-15 12:35:23 +03:00
|
|
|
if (intel_crtc_has_dp_encoder(crtc_state))
|
2021-02-05 16:48:40 +02:00
|
|
|
coreclk |= 0x01000000;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW7(ch), coreclk);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, VLV_PLL_DW19(ch), 0x87871000);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_put(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:26 +03:00
|
|
|
static void _vlv_enable_pll(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:26 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2021-07-15 12:35:26 +03:00
|
|
|
enum pipe pipe = crtc->pipe;
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
|
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-07-15 12:35:26 +03:00
|
|
|
udelay(150);
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (intel_de_wait_for_set(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
|
|
|
|
drm_err(display->drm, "DPLL %d failed to lock\n", pipe);
|
2021-07-15 12:35:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void vlv_enable_pll(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2024-08-30 13:15:45 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:26 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2021-07-15 12:35:26 +03:00
|
|
|
enum pipe pipe = crtc->pipe;
|
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* PLL is protected by panel, make sure we can write it */
|
2024-08-30 13:15:45 +03:00
|
|
|
assert_pps_unlocked(display, pipe);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* Enable Refclk */
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe),
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->dpll & ~(DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV));
|
2021-07-15 12:35:26 +03:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
if (hw_state->dpll & DPLL_VCO_ENABLE) {
|
2021-07-15 12:35:26 +03:00
|
|
|
vlv_prepare_pll(crtc_state);
|
|
|
|
_vlv_enable_pll(crtc_state);
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL_MD(display, pipe), hw_state->dpll_md);
|
|
|
|
intel_de_posting_read(display, DPLL_MD(display, pipe));
|
2021-07-15 12:35:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void chv_prepare_pll(const struct intel_crtc_state *crtc_state)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2025-05-12 17:56:57 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:23 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-22 11:34:48 +03:00
|
|
|
const struct dpll *clock = &crtc_state->dpll;
|
2024-04-22 11:34:51 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(crtc->pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe);
|
2024-04-22 11:34:49 +03:00
|
|
|
u32 tmp, loopfilter, tribuf_calcntr;
|
2024-04-22 11:34:48 +03:00
|
|
|
u32 m2_frac;
|
|
|
|
|
|
|
|
m2_frac = clock->m2 & 0x3fffff;
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* p1 and p2 divider */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_CMN_DW13(ch),
|
2024-04-22 11:34:56 +03:00
|
|
|
DPIO_CHV_S1_DIV(5) |
|
|
|
|
DPIO_CHV_P1_DIV(clock->p1) |
|
|
|
|
DPIO_CHV_P2_DIV(clock->p2) |
|
|
|
|
DPIO_CHV_K_DIV(1));
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Feedback post-divider - m2 */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW0(ch),
|
2024-04-22 11:34:56 +03:00
|
|
|
DPIO_CHV_M2_DIV(clock->m2 >> 22));
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Feedback refclk divider - n and m1 */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW1(ch),
|
2024-04-22 11:34:56 +03:00
|
|
|
DPIO_CHV_M1_DIV(DPIO_CHV_M1_DIV_BY_2) |
|
|
|
|
DPIO_CHV_N_DIV(1));
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* M2 fraction division */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW2(ch),
|
2024-04-22 11:34:56 +03:00
|
|
|
DPIO_CHV_M2_FRAC_DIV(m2_frac));
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* M2 fraction division enable */
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, CHV_PLL_DW3(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= ~(DPIO_CHV_FEEDFWD_GAIN_MASK | DPIO_CHV_FRAC_DIV_EN);
|
2024-04-22 11:34:56 +03:00
|
|
|
tmp |= DPIO_CHV_FEEDFWD_GAIN(2);
|
2024-04-22 11:34:48 +03:00
|
|
|
if (m2_frac)
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp |= DPIO_CHV_FRAC_DIV_EN;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW3(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Program digital lock detect threshold */
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, CHV_PLL_DW9(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= ~(DPIO_CHV_INT_LOCK_THRESHOLD_MASK |
|
2024-04-22 11:34:56 +03:00
|
|
|
DPIO_CHV_INT_LOCK_THRESHOLD_SEL_COARSE);
|
|
|
|
tmp |= DPIO_CHV_INT_LOCK_THRESHOLD(0x5);
|
2024-04-22 11:34:48 +03:00
|
|
|
if (!m2_frac)
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp |= DPIO_CHV_INT_LOCK_THRESHOLD_SEL_COARSE;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW9(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Loop filter */
|
2024-04-22 11:34:48 +03:00
|
|
|
if (clock->vco == 5400000) {
|
2024-04-22 11:34:56 +03:00
|
|
|
loopfilter = DPIO_CHV_PROP_COEFF(0x3) |
|
|
|
|
DPIO_CHV_INT_COEFF(0x8) |
|
|
|
|
DPIO_CHV_GAIN_CTRL(0x1);
|
2021-02-05 16:48:40 +02:00
|
|
|
tribuf_calcntr = 0x9;
|
2024-04-22 11:34:48 +03:00
|
|
|
} else if (clock->vco <= 6200000) {
|
2024-04-22 11:34:56 +03:00
|
|
|
loopfilter = DPIO_CHV_PROP_COEFF(0x5) |
|
|
|
|
DPIO_CHV_INT_COEFF(0xB) |
|
|
|
|
DPIO_CHV_GAIN_CTRL(0x3);
|
2021-02-05 16:48:40 +02:00
|
|
|
tribuf_calcntr = 0x9;
|
2024-04-22 11:34:48 +03:00
|
|
|
} else if (clock->vco <= 6480000) {
|
2024-04-22 11:34:56 +03:00
|
|
|
loopfilter = DPIO_CHV_PROP_COEFF(0x4) |
|
|
|
|
DPIO_CHV_INT_COEFF(0x9) |
|
|
|
|
DPIO_CHV_GAIN_CTRL(0x3);
|
2021-02-05 16:48:40 +02:00
|
|
|
tribuf_calcntr = 0x8;
|
|
|
|
} else {
|
|
|
|
/* Not supported. Apply the same limits as in the max case */
|
2024-04-22 11:34:56 +03:00
|
|
|
loopfilter = DPIO_CHV_PROP_COEFF(0x4) |
|
|
|
|
DPIO_CHV_INT_COEFF(0x9) |
|
|
|
|
DPIO_CHV_GAIN_CTRL(0x3);
|
2021-02-05 16:48:40 +02:00
|
|
|
tribuf_calcntr = 0;
|
|
|
|
}
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW6(ch), loopfilter);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, CHV_PLL_DW8(ch));
|
2024-04-22 11:34:49 +03:00
|
|
|
tmp &= ~DPIO_CHV_TDC_TARGET_CNT_MASK;
|
2024-04-22 11:34:56 +03:00
|
|
|
tmp |= DPIO_CHV_TDC_TARGET_CNT(tribuf_calcntr);
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_PLL_DW8(ch), tmp);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* AFC Recal */
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_CMN_DW14(ch),
|
|
|
|
vlv_dpio_read(display->drm, phy, CHV_CMN_DW14(ch)) |
|
2024-04-22 11:34:50 +03:00
|
|
|
DPIO_AFC_RECAL);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_put(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2021-07-15 12:35:26 +03:00
|
|
|
static void _chv_enable_pll(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:26 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2024-04-22 11:34:51 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(crtc->pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe);
|
2024-04-22 11:34:51 +03:00
|
|
|
enum pipe pipe = crtc->pipe;
|
2021-07-15 12:35:26 +03:00
|
|
|
u32 tmp;
|
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* Enable back the 10bit clock to display controller */
|
2025-05-12 17:56:57 +03:00
|
|
|
tmp = vlv_dpio_read(display->drm, phy, CHV_CMN_DW14(ch));
|
2021-07-15 12:35:26 +03:00
|
|
|
tmp |= DPIO_DCLKP_EN;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_CMN_DW14(ch), tmp);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_put(display->drm);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Need to wait > 100ns between dclkp clock enable bit and PLL enable.
|
|
|
|
*/
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
/* Enable PLL */
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* Check PLL is locked */
|
2025-03-25 14:36:33 +02:00
|
|
|
if (intel_de_wait_for_set(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
|
|
|
|
drm_err(display->drm, "PLL %d failed to lock\n", pipe);
|
2021-07-15 12:35:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void chv_enable_pll(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2024-08-30 13:15:45 +03:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-07-15 12:35:26 +03:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
2024-04-12 21:27:01 +03:00
|
|
|
const struct i9xx_dpll_hw_state *hw_state = &crtc_state->dpll_hw_state.i9xx;
|
2021-07-15 12:35:26 +03:00
|
|
|
enum pipe pipe = crtc->pipe;
|
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* PLL is protected by panel, make sure we can write it */
|
2024-08-30 13:15:45 +03:00
|
|
|
assert_pps_unlocked(display, pipe);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/* Enable Refclk and SSC */
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe),
|
2024-04-12 21:27:00 +03:00
|
|
|
hw_state->dpll & ~DPLL_VCO_ENABLE);
|
2021-07-15 12:35:26 +03:00
|
|
|
|
2024-04-12 21:27:00 +03:00
|
|
|
if (hw_state->dpll & DPLL_VCO_ENABLE) {
|
2021-07-15 12:35:26 +03:00
|
|
|
chv_prepare_pll(crtc_state);
|
|
|
|
_chv_enable_pll(crtc_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipe != PIPE_A) {
|
|
|
|
/*
|
|
|
|
* WaPixelRepeatModeFixForC0:chv
|
|
|
|
*
|
|
|
|
* DPLLCMD is AWOL. Use chicken bits to propagate
|
|
|
|
* the value from DPLLBMD to either pipe B or C.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, CBR4_VLV, CBR_DPLLBMD_PIPE(pipe));
|
|
|
|
intel_de_write(display, DPLL_MD(display, PIPE_B),
|
2024-06-04 18:25:20 +03:00
|
|
|
hw_state->dpll_md);
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, CBR4_VLV, 0);
|
|
|
|
display->state.chv_dpll_md[pipe] = hw_state->dpll_md;
|
2021-07-15 12:35:26 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* DPLLB VGA mode also seems to cause problems.
|
|
|
|
* We should always have it disabled.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
drm_WARN_ON(display->drm,
|
|
|
|
(intel_de_read(display, DPLL(display, PIPE_B)) &
|
2021-07-15 12:35:26 +03:00
|
|
|
DPLL_VGA_MODE_DIS) == 0);
|
|
|
|
} else {
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_write(display, DPLL_MD(display, pipe),
|
2024-06-04 18:25:20 +03:00
|
|
|
hw_state->dpll_md);
|
2025-03-25 14:36:33 +02:00
|
|
|
intel_de_posting_read(display, DPLL_MD(display, pipe));
|
2021-07-15 12:35:26 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-05 16:48:40 +02:00
|
|
|
/**
|
|
|
|
* vlv_force_pll_on - forcibly enable just the PLL
|
2025-03-25 14:36:33 +02:00
|
|
|
* @display: display device
|
2021-02-05 16:48:40 +02:00
|
|
|
* @pipe: pipe PLL to enable
|
|
|
|
* @dpll: PLL configuration
|
|
|
|
*
|
|
|
|
* Enable the PLL for @pipe using the supplied @dpll config. To be used
|
|
|
|
* in cases where we need the PLL enabled even when @pipe is not going to
|
|
|
|
* be enabled.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
int vlv_force_pll_on(struct intel_display *display, enum pipe pipe,
|
2021-02-05 16:48:40 +02:00
|
|
|
const struct dpll *dpll)
|
|
|
|
{
|
2024-09-04 16:06:32 +03:00
|
|
|
struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe);
|
2021-07-15 12:35:23 +03:00
|
|
|
struct intel_crtc_state *crtc_state;
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2021-07-15 12:35:23 +03:00
|
|
|
crtc_state = intel_crtc_state_alloc(crtc);
|
|
|
|
if (!crtc_state)
|
2021-02-05 16:48:40 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-07-15 12:35:23 +03:00
|
|
|
crtc_state->cpu_transcoder = (enum transcoder)pipe;
|
|
|
|
crtc_state->pixel_multiplier = 1;
|
|
|
|
crtc_state->dpll = *dpll;
|
|
|
|
crtc_state->output_types = BIT(INTEL_OUTPUT_EDP);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.cherryview) {
|
2021-07-15 12:35:23 +03:00
|
|
|
chv_compute_dpll(crtc_state);
|
|
|
|
chv_enable_pll(crtc_state);
|
2021-02-05 16:48:40 +02:00
|
|
|
} else {
|
2021-07-15 12:35:23 +03:00
|
|
|
vlv_compute_dpll(crtc_state);
|
|
|
|
vlv_enable_pll(crtc_state);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2023-10-11 00:01:01 +05:30
|
|
|
intel_crtc_destroy_state(&crtc->base, &crtc_state->uapi);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
void vlv_disable_pll(struct intel_display *display, enum pipe pipe)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Make sure the pipe isn't still relying on us */
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, (enum transcoder)pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
val = DPLL_INTEGRATED_REF_CLK_VLV |
|
|
|
|
DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
|
|
|
|
if (pipe != PIPE_A)
|
|
|
|
val |= DPLL_INTEGRATED_CRI_CLK_VLV;
|
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), val);
|
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
2025-03-25 14:36:33 +02:00
|
|
|
void chv_disable_pll(struct intel_display *display, enum pipe pipe)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2024-04-22 11:34:50 +03:00
|
|
|
enum dpio_channel ch = vlv_pipe_to_channel(pipe);
|
2023-11-14 12:45:34 +02:00
|
|
|
enum dpio_phy phy = vlv_pipe_to_phy(pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Make sure the pipe isn't still relying on us */
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, (enum transcoder)pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
val = DPLL_SSC_REF_CLK_CHV |
|
|
|
|
DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
|
|
|
|
if (pipe != PIPE_A)
|
|
|
|
val |= DPLL_INTEGRATED_CRI_CLK_VLV;
|
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), val);
|
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_get(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
|
|
|
/* Disable 10bit clock to display controller */
|
2025-05-12 17:56:57 +03:00
|
|
|
val = vlv_dpio_read(display->drm, phy, CHV_CMN_DW14(ch));
|
2021-02-05 16:48:40 +02:00
|
|
|
val &= ~DPIO_DCLKP_EN;
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_write(display->drm, phy, CHV_CMN_DW14(ch), val);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-05-12 17:56:57 +03:00
|
|
|
vlv_dpio_put(display->drm);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void i9xx_disable_pll(const struct intel_crtc_state *crtc_state)
|
|
|
|
{
|
2025-02-12 18:36:33 +02:00
|
|
|
struct intel_display *display = to_intel_display(crtc_state);
|
2021-02-05 16:48:40 +02:00
|
|
|
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
|
|
|
|
enum pipe pipe = crtc->pipe;
|
|
|
|
|
|
|
|
/* Don't disable pipe or pipe PLLs if needed */
|
2025-02-12 18:36:33 +02:00
|
|
|
if (display->platform.i830)
|
2021-02-05 16:48:40 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Make sure the pipe isn't still relying on us */
|
2025-02-12 18:36:33 +02:00
|
|
|
assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
|
2021-02-05 16:48:40 +02:00
|
|
|
|
2025-02-12 18:36:33 +02:00
|
|
|
intel_de_write(display, DPLL(display, pipe), DPLL_VGA_MODE_DIS);
|
|
|
|
intel_de_posting_read(display, DPLL(display, pipe));
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vlv_force_pll_off - forcibly disable just the PLL
|
2025-03-25 14:36:33 +02:00
|
|
|
* @display: display device
|
2021-02-05 16:48:40 +02:00
|
|
|
* @pipe: pipe PLL to disable
|
|
|
|
*
|
|
|
|
* Disable the PLL for @pipe. To be used in cases where we need
|
|
|
|
* the PLL enabled even when @pipe is not going to be enabled.
|
|
|
|
*/
|
2025-03-25 14:36:33 +02:00
|
|
|
void vlv_force_pll_off(struct intel_display *display, enum pipe pipe)
|
2021-02-05 16:48:40 +02:00
|
|
|
{
|
2025-03-25 14:36:33 +02:00
|
|
|
if (display->platform.cherryview)
|
|
|
|
chv_disable_pll(display, pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
else
|
2025-03-25 14:36:33 +02:00
|
|
|
vlv_disable_pll(display, pipe);
|
2021-02-05 16:48:40 +02:00
|
|
|
}
|
2021-09-30 12:23:00 +03:00
|
|
|
|
|
|
|
/* Only for pre-ILK configs */
|
2025-02-12 13:15:39 +05:30
|
|
|
static void assert_pll(struct intel_display *display,
|
2021-09-30 12:23:00 +03:00
|
|
|
enum pipe pipe, bool state)
|
|
|
|
{
|
|
|
|
bool cur_state;
|
|
|
|
|
2024-10-24 19:25:10 +03:00
|
|
|
cur_state = intel_de_read(display, DPLL(display, pipe)) & DPLL_VCO_ENABLE;
|
|
|
|
INTEL_DISPLAY_STATE_WARN(display, cur_state != state,
|
|
|
|
"PLL state assertion failure (expected %s, current %s)\n",
|
|
|
|
str_on_off(state), str_on_off(cur_state));
|
2021-09-30 12:23:00 +03:00
|
|
|
}
|
|
|
|
|
2025-02-12 13:15:39 +05:30
|
|
|
void assert_pll_enabled(struct intel_display *display, enum pipe pipe)
|
2021-09-30 12:23:00 +03:00
|
|
|
{
|
2025-02-12 13:15:39 +05:30
|
|
|
assert_pll(display, pipe, true);
|
2021-09-30 12:23:00 +03:00
|
|
|
}
|
|
|
|
|
2025-02-12 13:15:39 +05:30
|
|
|
void assert_pll_disabled(struct intel_display *display, enum pipe pipe)
|
2021-09-30 12:23:00 +03:00
|
|
|
{
|
2025-02-12 13:15:39 +05:30
|
|
|
assert_pll(display, pipe, false);
|
2021-09-30 12:23:00 +03:00
|
|
|
}
|