diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/video/fbdev/aty/mach64_gx.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to '')
-rw-r--r-- | drivers/video/fbdev/aty/mach64_gx.c | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/drivers/video/fbdev/aty/mach64_gx.c b/drivers/video/fbdev/aty/mach64_gx.c new file mode 100644 index 000000000..d06d24830 --- /dev/null +++ b/drivers/video/fbdev/aty/mach64_gx.c @@ -0,0 +1,908 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * ATI Mach64 GX Support + */ + +#include <linux/delay.h> +#include <linux/fb.h> + +#include <asm/io.h> + +#include <video/mach64.h> +#include "atyfb.h" + +/* Definitions for the ICS 2595 == ATI 18818_1 Clockchip */ + +#define REF_FREQ_2595 1432 /* 14.33 MHz (exact 14.31818) */ +#define REF_DIV_2595 46 /* really 43 on ICS 2595 !!! */ + /* ohne Prescaler */ +#define MAX_FREQ_2595 15938 /* 159.38 MHz (really 170.486) */ +#define MIN_FREQ_2595 8000 /* 80.00 MHz ( 85.565) */ + /* mit Prescaler 2, 4, 8 */ +#define ABS_MIN_FREQ_2595 1000 /* 10.00 MHz (really 10.697) */ +#define N_ADJ_2595 257 + +#define STOP_BITS_2595 0x1800 + + +#define MIN_N_408 2 + +#define MIN_N_1703 6 + +#define MIN_M 2 +#define MAX_M 30 +#define MIN_N 35 +#define MAX_N 255-8 + + + /* + * Support Functions + */ + +static void aty_dac_waste4(const struct atyfb_par *par) +{ + (void) aty_ld_8(DAC_REGS, par); + + (void) aty_ld_8(DAC_REGS + 2, par); + (void) aty_ld_8(DAC_REGS + 2, par); + (void) aty_ld_8(DAC_REGS + 2, par); + (void) aty_ld_8(DAC_REGS + 2, par); +} + +static void aty_StrobeClock(const struct atyfb_par *par) +{ + u8 tmp; + + udelay(26); + + tmp = aty_ld_8(CLOCK_CNTL, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, tmp | CLOCK_STROBE, par); + return; +} + + + /* + * IBM RGB514 DAC and Clock Chip + */ + +static void aty_st_514(int offset, u8 val, const struct atyfb_par *par) +{ + aty_st_8(DAC_CNTL, 1, par); + /* right addr byte */ + aty_st_8(DAC_W_INDEX, offset & 0xff, par); + /* left addr byte */ + aty_st_8(DAC_DATA, (offset >> 8) & 0xff, par); + aty_st_8(DAC_MASK, val, par); + aty_st_8(DAC_CNTL, 0, par); +} + +static int aty_set_dac_514(const struct fb_info *info, + const union aty_pll *pll, u32 bpp, u32 accel) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + static struct { + u8 pixel_dly; + u8 misc2_cntl; + u8 pixel_rep; + u8 pixel_cntl_index; + u8 pixel_cntl_v1; + } tab[3] = { + { + 0, 0x41, 0x03, 0x71, 0x45}, /* 8 bpp */ + { + 0, 0x45, 0x04, 0x0c, 0x01}, /* 555 */ + { + 0, 0x45, 0x06, 0x0e, 0x00}, /* XRGB */ + }; + int i; + + switch (bpp) { + case 8: + default: + i = 0; + break; + case 16: + i = 1; + break; + case 32: + i = 2; + break; + } + aty_st_514(0x90, 0x00, par); /* VRAM Mask Low */ + aty_st_514(0x04, tab[i].pixel_dly, par); /* Horizontal Sync Control */ + aty_st_514(0x05, 0x00, par); /* Power Management */ + aty_st_514(0x02, 0x01, par); /* Misc Clock Control */ + aty_st_514(0x71, tab[i].misc2_cntl, par); /* Misc Control 2 */ + aty_st_514(0x0a, tab[i].pixel_rep, par); /* Pixel Format */ + aty_st_514(tab[i].pixel_cntl_index, tab[i].pixel_cntl_v1, par); + /* Misc Control 2 / 16 BPP Control / 32 BPP Control */ + return 0; +} + +static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) +{ + /* + * FIXME: use real calculations instead of using fixed values from the old + * driver + */ + static struct { + u32 limit; /* pixlock rounding limit (arbitrary) */ + u8 m; /* (df<<6) | vco_div_count */ + u8 n; /* ref_div_count */ + } RGB514_clocks[7] = { + { + 8000, (3 << 6) | 20, 9}, /* 7395 ps / 135.2273 MHz */ + { + 10000, (1 << 6) | 19, 3}, /* 9977 ps / 100.2273 MHz */ + { + 13000, (1 << 6) | 2, 3}, /* 12509 ps / 79.9432 MHz */ + { + 14000, (2 << 6) | 8, 7}, /* 13394 ps / 74.6591 MHz */ + { + 16000, (1 << 6) | 44, 6}, /* 15378 ps / 65.0284 MHz */ + { + 25000, (1 << 6) | 15, 5}, /* 17460 ps / 57.2727 MHz */ + { + 50000, (0 << 6) | 53, 7}, /* 33145 ps / 30.1705 MHz */ + }; + int i; + + for (i = 0; i < ARRAY_SIZE(RGB514_clocks); i++) + if (vclk_per <= RGB514_clocks[i].limit) { + pll->ibm514.m = RGB514_clocks[i].m; + pll->ibm514.n = RGB514_clocks[i].n; + return 0; + } + return -EINVAL; +} + +static u32 aty_pll_514_to_var(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u8 df, vco_div_count, ref_div_count; + + df = pll->ibm514.m >> 6; + vco_div_count = pll->ibm514.m & 0x3f; + ref_div_count = pll->ibm514.n; + + return ((par->ref_clk_per * ref_div_count) << (3 - df))/ + (vco_div_count + 65); +} + +static void aty_set_pll_514(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + + aty_st_514(0x06, 0x02, par); /* DAC Operation */ + aty_st_514(0x10, 0x01, par); /* PLL Control 1 */ + aty_st_514(0x70, 0x01, par); /* Misc Control 1 */ + aty_st_514(0x8f, 0x1f, par); /* PLL Ref. Divider Input */ + aty_st_514(0x03, 0x00, par); /* Sync Control */ + aty_st_514(0x05, 0x00, par); /* Power Management */ + aty_st_514(0x20, pll->ibm514.m, par); /* F0 / M0 */ + aty_st_514(0x21, pll->ibm514.n, par); /* F1 / N0 */ +} + +const struct aty_dac_ops aty_dac_ibm514 = { + .set_dac = aty_set_dac_514, +}; + +const struct aty_pll_ops aty_pll_ibm514 = { + .var_to_pll = aty_var_to_pll_514, + .pll_to_var = aty_pll_514_to_var, + .set_pll = aty_set_pll_514, +}; + + + /* + * ATI 68860-B DAC + */ + +static int aty_set_dac_ATI68860_B(const struct fb_info *info, + const union aty_pll *pll, u32 bpp, + u32 accel) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 gModeReg, devSetupRegA, temp, mask; + + gModeReg = 0; + devSetupRegA = 0; + + switch (bpp) { + case 8: + gModeReg = 0x83; + devSetupRegA = + 0x60 | 0x00 /*(info->mach64DAC8Bit ? 0x00 : 0x01) */ ; + break; + case 15: + gModeReg = 0xA0; + devSetupRegA = 0x60; + break; + case 16: + gModeReg = 0xA1; + devSetupRegA = 0x60; + break; + case 24: + gModeReg = 0xC0; + devSetupRegA = 0x60; + break; + case 32: + gModeReg = 0xE3; + devSetupRegA = 0x60; + break; + } + + if (!accel) { + gModeReg = 0x80; + devSetupRegA = 0x61; + } + + temp = aty_ld_8(DAC_CNTL, par); + aty_st_8(DAC_CNTL, (temp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3, + par); + + aty_st_8(DAC_REGS + 2, 0x1D, par); + aty_st_8(DAC_REGS + 3, gModeReg, par); + aty_st_8(DAC_REGS, 0x02, par); + + temp = aty_ld_8(DAC_CNTL, par); + aty_st_8(DAC_CNTL, temp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par); + + if (info->fix.smem_len < ONE_MB) + mask = 0x04; + else if (info->fix.smem_len == ONE_MB) + mask = 0x08; + else + mask = 0x0C; + + /* The following assumes that the BIOS has correctly set R7 of the + * Device Setup Register A at boot time. + */ +#define A860_DELAY_L 0x80 + + temp = aty_ld_8(DAC_REGS, par); + aty_st_8(DAC_REGS, (devSetupRegA | mask) | (temp & A860_DELAY_L), + par); + temp = aty_ld_8(DAC_CNTL, par); + aty_st_8(DAC_CNTL, (temp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)), + par); + + aty_st_le32(BUS_CNTL, 0x890e20f1, par); + aty_st_le32(DAC_CNTL, 0x47052100, par); + return 0; +} + +const struct aty_dac_ops aty_dac_ati68860b = { + .set_dac = aty_set_dac_ATI68860_B, +}; + + + /* + * AT&T 21C498 DAC + */ + +static int aty_set_dac_ATT21C498(const struct fb_info *info, + const union aty_pll *pll, u32 bpp, + u32 accel) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 dotClock; + int muxmode = 0; + int DACMask = 0; + + dotClock = 100000000 / pll->ics2595.period_in_ps; + + switch (bpp) { + case 8: + if (dotClock > 8000) { + DACMask = 0x24; + muxmode = 1; + } else + DACMask = 0x04; + break; + case 15: + DACMask = 0x16; + break; + case 16: + DACMask = 0x36; + break; + case 24: + DACMask = 0xE6; + break; + case 32: + DACMask = 0xE6; + break; + } + + if (1 /* info->mach64DAC8Bit */ ) + DACMask |= 0x02; + + aty_dac_waste4(par); + aty_st_8(DAC_REGS + 2, DACMask, par); + + aty_st_le32(BUS_CNTL, 0x890e20f1, par); + aty_st_le32(DAC_CNTL, 0x00072000, par); + return muxmode; +} + +const struct aty_dac_ops aty_dac_att21c498 = { + .set_dac = aty_set_dac_ATT21C498, +}; + + + /* + * ATI 18818 / ICS 2595 Clock Chip + */ + +static int aty_var_to_pll_18818(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) +{ + u32 MHz100; /* in 0.01 MHz */ + u32 program_bits; + u32 post_divider; + + /* Calculate the programming word */ + MHz100 = 100000000 / vclk_per; + + program_bits = -1; + post_divider = 1; + + if (MHz100 > MAX_FREQ_2595) { + return -EINVAL; + } else if (MHz100 < ABS_MIN_FREQ_2595) { + return -EINVAL; + } else { + while (MHz100 < MIN_FREQ_2595) { + MHz100 *= 2; + post_divider *= 2; + } + } + MHz100 *= 1000; + MHz100 = (REF_DIV_2595 * MHz100) / REF_FREQ_2595; + + MHz100 += 500; /* + 0.5 round */ + MHz100 /= 1000; + + if (program_bits == -1) { + program_bits = MHz100 - N_ADJ_2595; + switch (post_divider) { + case 1: + program_bits |= 0x0600; + break; + case 2: + program_bits |= 0x0400; + break; + case 4: + program_bits |= 0x0200; + break; + case 8: + default: + break; + } + } + + program_bits |= STOP_BITS_2595; + + pll->ics2595.program_bits = program_bits; + pll->ics2595.locationAddr = 0; + pll->ics2595.post_divider = post_divider; + pll->ics2595.period_in_ps = vclk_per; + + return 0; +} + +static u32 aty_pll_18818_to_var(const struct fb_info *info, + const union aty_pll *pll) +{ + return (pll->ics2595.period_in_ps); /* default for now */ +} + +static void aty_ICS2595_put1bit(u8 data, const struct atyfb_par *par) +{ + u8 tmp; + + data &= 0x01; + tmp = aty_ld_8(CLOCK_CNTL, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, + (tmp & ~0x04) | (data << 2), par); + + tmp = aty_ld_8(CLOCK_CNTL, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (0 << 3), + par); + + aty_StrobeClock(par); + + tmp = aty_ld_8(CLOCK_CNTL, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (1 << 3), + par); + + aty_StrobeClock(par); + return; +} + +static void aty_set_pll18818(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 program_bits; + u32 locationAddr; + + u32 i; + + u8 old_clock_cntl; + u8 old_crtc_ext_disp; + + old_clock_cntl = aty_ld_8(CLOCK_CNTL, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par); + + old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par); + aty_st_8(CRTC_GEN_CNTL + 3, + old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par); + + mdelay(15); /* delay for 50 (15) ms */ + + program_bits = pll->ics2595.program_bits; + locationAddr = pll->ics2595.locationAddr; + + /* Program the clock chip */ + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par); /* Strobe = 0 */ + aty_StrobeClock(par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 1, par); /* Strobe = 0 */ + aty_StrobeClock(par); + + aty_ICS2595_put1bit(1, par); /* Send start bits */ + aty_ICS2595_put1bit(0, par); /* Start bit */ + aty_ICS2595_put1bit(0, par); /* Read / ~Write */ + + for (i = 0; i < 5; i++) { /* Location 0..4 */ + aty_ICS2595_put1bit(locationAddr & 1, par); + locationAddr >>= 1; + } + + for (i = 0; i < 8 + 1 + 2 + 2; i++) { + aty_ICS2595_put1bit(program_bits & 1, par); + program_bits >>= 1; + } + + mdelay(1); /* delay for 1 ms */ + + (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */ + aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par); + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, + old_clock_cntl | CLOCK_STROBE, par); + + mdelay(50); /* delay for 50 (15) ms */ + aty_st_8(CLOCK_CNTL + par->clk_wr_offset, + ((pll->ics2595.locationAddr & 0x0F) | CLOCK_STROBE), par); + return; +} + +const struct aty_pll_ops aty_pll_ati18818_1 = { + .var_to_pll = aty_var_to_pll_18818, + .pll_to_var = aty_pll_18818_to_var, + .set_pll = aty_set_pll18818, +}; + + + /* + * STG 1703 Clock Chip + */ + +static int aty_var_to_pll_1703(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) +{ + u32 mhz100; /* in 0.01 MHz */ + u32 program_bits; + /* u32 post_divider; */ + u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq; + u32 temp, tempB; + u16 remainder, preRemainder; + short divider = 0, tempA; + + /* Calculate the programming word */ + mhz100 = 100000000 / vclk_per; + mach64MinFreq = MIN_FREQ_2595; + mach64MaxFreq = MAX_FREQ_2595; + mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */ + + /* Calculate program word */ + if (mhz100 == 0) + program_bits = 0xE0; + else { + if (mhz100 < mach64MinFreq) + mhz100 = mach64MinFreq; + if (mhz100 > mach64MaxFreq) + mhz100 = mach64MaxFreq; + + divider = 0; + while (mhz100 < (mach64MinFreq << 3)) { + mhz100 <<= 1; + divider += 0x20; + } + + temp = (unsigned int) (mhz100); + temp = (unsigned int) (temp * (MIN_N_1703 + 2)); + temp -= (short) (mach64RefFreq << 1); + + tempA = MIN_N_1703; + preRemainder = 0xffff; + + do { + tempB = temp; + remainder = tempB % mach64RefFreq; + tempB = tempB / mach64RefFreq; + + if ((tempB & 0xffff) <= 127 + && (remainder <= preRemainder)) { + preRemainder = remainder; + divider &= ~0x1f; + divider |= tempA; + divider = + (divider & 0x00ff) + + ((tempB & 0xff) << 8); + } + + temp += mhz100; + tempA++; + } while (tempA <= (MIN_N_1703 << 1)); + + program_bits = divider; + } + + pll->ics2595.program_bits = program_bits; + pll->ics2595.locationAddr = 0; + pll->ics2595.post_divider = divider; /* fuer nix */ + pll->ics2595.period_in_ps = vclk_per; + + return 0; +} + +static u32 aty_pll_1703_to_var(const struct fb_info *info, + const union aty_pll *pll) +{ + return (pll->ics2595.period_in_ps); /* default for now */ +} + +static void aty_set_pll_1703(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 program_bits; + u32 locationAddr; + + char old_crtc_ext_disp; + + old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par); + aty_st_8(CRTC_GEN_CNTL + 3, + old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par); + + program_bits = pll->ics2595.program_bits; + locationAddr = pll->ics2595.locationAddr; + + /* Program clock */ + aty_dac_waste4(par); + + (void) aty_ld_8(DAC_REGS + 2, par); + aty_st_8(DAC_REGS + 2, (locationAddr << 1) + 0x20, par); + aty_st_8(DAC_REGS + 2, 0, par); + aty_st_8(DAC_REGS + 2, (program_bits & 0xFF00) >> 8, par); + aty_st_8(DAC_REGS + 2, (program_bits & 0xFF), par); + + (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */ + aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par); + return; +} + +const struct aty_pll_ops aty_pll_stg1703 = { + .var_to_pll = aty_var_to_pll_1703, + .pll_to_var = aty_pll_1703_to_var, + .set_pll = aty_set_pll_1703, +}; + + + /* + * Chrontel 8398 Clock Chip + */ + +static int aty_var_to_pll_8398(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) +{ + u32 tempA, tempB, fOut, longMHz100, diff, preDiff; + + u32 mhz100; /* in 0.01 MHz */ + u32 program_bits; + /* u32 post_divider; */ + u32 mach64MinFreq, mach64MaxFreq; + u16 m, n, k = 0, save_m, save_n, twoToKth; + + /* Calculate the programming word */ + mhz100 = 100000000 / vclk_per; + mach64MinFreq = MIN_FREQ_2595; + mach64MaxFreq = MAX_FREQ_2595; + + save_m = 0; + save_n = 0; + + /* Calculate program word */ + if (mhz100 == 0) + program_bits = 0xE0; + else { + if (mhz100 < mach64MinFreq) + mhz100 = mach64MinFreq; + if (mhz100 > mach64MaxFreq) + mhz100 = mach64MaxFreq; + + longMHz100 = mhz100 * 256 / 100; /* 8 bit scale this */ + + while (mhz100 < (mach64MinFreq << 3)) { + mhz100 <<= 1; + k++; + } + + twoToKth = 1 << k; + diff = 0; + preDiff = 0xFFFFFFFF; + + for (m = MIN_M; m <= MAX_M; m++) { + for (n = MIN_N; n <= MAX_N; n++) { + tempA = 938356; /* 14.31818 * 65536 */ + tempA *= (n + 8); /* 43..256 */ + tempB = twoToKth * 256; + tempB *= (m + 2); /* 4..32 */ + fOut = tempA / tempB; /* 8 bit scale */ + + if (longMHz100 > fOut) + diff = longMHz100 - fOut; + else + diff = fOut - longMHz100; + + if (diff < preDiff) { + save_m = m; + save_n = n; + preDiff = diff; + } + } + } + + program_bits = (k << 6) + (save_m) + (save_n << 8); + } + + pll->ics2595.program_bits = program_bits; + pll->ics2595.locationAddr = 0; + pll->ics2595.post_divider = 0; + pll->ics2595.period_in_ps = vclk_per; + + return 0; +} + +static u32 aty_pll_8398_to_var(const struct fb_info *info, + const union aty_pll *pll) +{ + return (pll->ics2595.period_in_ps); /* default for now */ +} + +static void aty_set_pll_8398(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 program_bits; + u32 locationAddr; + + char old_crtc_ext_disp; + char tmp; + + old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par); + aty_st_8(CRTC_GEN_CNTL + 3, + old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par); + + program_bits = pll->ics2595.program_bits; + locationAddr = pll->ics2595.locationAddr; + + /* Program clock */ + tmp = aty_ld_8(DAC_CNTL, par); + aty_st_8(DAC_CNTL, tmp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par); + + aty_st_8(DAC_REGS, locationAddr, par); + aty_st_8(DAC_REGS + 1, (program_bits & 0xff00) >> 8, par); + aty_st_8(DAC_REGS + 1, (program_bits & 0xff), par); + + tmp = aty_ld_8(DAC_CNTL, par); + aty_st_8(DAC_CNTL, (tmp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3, + par); + + (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */ + aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par); + + return; +} + +const struct aty_pll_ops aty_pll_ch8398 = { + .var_to_pll = aty_var_to_pll_8398, + .pll_to_var = aty_pll_8398_to_var, + .set_pll = aty_set_pll_8398, +}; + + + /* + * AT&T 20C408 Clock Chip + */ + +static int aty_var_to_pll_408(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) +{ + u32 mhz100; /* in 0.01 MHz */ + u32 program_bits; + /* u32 post_divider; */ + u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq; + u32 temp, tempB; + u16 remainder, preRemainder; + short divider = 0, tempA; + + /* Calculate the programming word */ + mhz100 = 100000000 / vclk_per; + mach64MinFreq = MIN_FREQ_2595; + mach64MaxFreq = MAX_FREQ_2595; + mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */ + + /* Calculate program word */ + if (mhz100 == 0) + program_bits = 0xFF; + else { + if (mhz100 < mach64MinFreq) + mhz100 = mach64MinFreq; + if (mhz100 > mach64MaxFreq) + mhz100 = mach64MaxFreq; + + while (mhz100 < (mach64MinFreq << 3)) { + mhz100 <<= 1; + divider += 0x40; + } + + temp = (unsigned int) mhz100; + temp = (unsigned int) (temp * (MIN_N_408 + 2)); + temp -= ((short) (mach64RefFreq << 1)); + + tempA = MIN_N_408; + preRemainder = 0xFFFF; + + do { + tempB = temp; + remainder = tempB % mach64RefFreq; + tempB = tempB / mach64RefFreq; + if (((tempB & 0xFFFF) <= 255) + && (remainder <= preRemainder)) { + preRemainder = remainder; + divider &= ~0x3f; + divider |= tempA; + divider = + (divider & 0x00FF) + + ((tempB & 0xFF) << 8); + } + temp += mhz100; + tempA++; + } while (tempA <= 32); + + program_bits = divider; + } + + pll->ics2595.program_bits = program_bits; + pll->ics2595.locationAddr = 0; + pll->ics2595.post_divider = divider; /* fuer nix */ + pll->ics2595.period_in_ps = vclk_per; + + return 0; +} + +static u32 aty_pll_408_to_var(const struct fb_info *info, + const union aty_pll *pll) +{ + return (pll->ics2595.period_in_ps); /* default for now */ +} + +static void aty_set_pll_408(const struct fb_info *info, + const union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 program_bits; + u32 locationAddr; + + u8 tmpA, tmpB, tmpC; + char old_crtc_ext_disp; + + old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par); + aty_st_8(CRTC_GEN_CNTL + 3, + old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par); + + program_bits = pll->ics2595.program_bits; + locationAddr = pll->ics2595.locationAddr; + + /* Program clock */ + aty_dac_waste4(par); + tmpB = aty_ld_8(DAC_REGS + 2, par) | 1; + aty_dac_waste4(par); + aty_st_8(DAC_REGS + 2, tmpB, par); + + tmpA = tmpB; + tmpC = tmpA; + tmpA |= 8; + tmpB = 1; + + aty_st_8(DAC_REGS, tmpB, par); + aty_st_8(DAC_REGS + 2, tmpA, par); + + udelay(400); /* delay for 400 us */ + + locationAddr = (locationAddr << 2) + 0x40; + tmpB = locationAddr; + tmpA = program_bits >> 8; + + aty_st_8(DAC_REGS, tmpB, par); + aty_st_8(DAC_REGS + 2, tmpA, par); + + tmpB = locationAddr + 1; + tmpA = (u8) program_bits; + + aty_st_8(DAC_REGS, tmpB, par); + aty_st_8(DAC_REGS + 2, tmpA, par); + + tmpB = locationAddr + 2; + tmpA = 0x77; + + aty_st_8(DAC_REGS, tmpB, par); + aty_st_8(DAC_REGS + 2, tmpA, par); + + udelay(400); /* delay for 400 us */ + tmpA = tmpC & (~(1 | 8)); + tmpB = 1; + + aty_st_8(DAC_REGS, tmpB, par); + aty_st_8(DAC_REGS + 2, tmpA, par); + + (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */ + aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par); + return; +} + +const struct aty_pll_ops aty_pll_att20c408 = { + .var_to_pll = aty_var_to_pll_408, + .pll_to_var = aty_pll_408_to_var, + .set_pll = aty_set_pll_408, +}; + + + /* + * Unsupported DAC and Clock Chip + */ + +static int aty_set_dac_unsupported(const struct fb_info *info, + const union aty_pll *pll, u32 bpp, + u32 accel) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + + aty_st_le32(BUS_CNTL, 0x890e20f1, par); + aty_st_le32(DAC_CNTL, 0x47052100, par); + /* new in 2.2.3p1 from Geert. ???????? */ + aty_st_le32(BUS_CNTL, 0x590e10ff, par); + aty_st_le32(DAC_CNTL, 0x47012100, par); + return 0; +} + +static int dummy(void) +{ + return 0; +} + +const struct aty_dac_ops aty_dac_unsupported = { + .set_dac = aty_set_dac_unsupported, +}; + +const struct aty_pll_ops aty_pll_unsupported = { + .var_to_pll = (void *) dummy, + .pll_to_var = (void *) dummy, + .set_pll = (void *) dummy, +}; |