1/* $NetBSD: videopll.c,v 1.4 2023/12/20 15:29:04 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2012 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28/* 29 * A driver for the iic-controlled PLLs used in early Apple onboard video 30 * hardware. For now we support /valkyrie only but others use very similar 31 * schemes to program their pixel clock so adding support for those should 32 * be simple enough. 33 */ 34 35#include <sys/cdefs.h> 36__KERNEL_RCSID(0, "$NetBSD: videopll.c,v 1.4 2023/12/20 15:29:04 thorpej Exp $"); 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/kernel.h> 41#include <sys/device.h> 42 43#include <dev/ofw/openfirm.h> 44#include <dev/i2c/i2cvar.h> 45#include <arch/macppc/dev/videopllvar.h> 46 47#include "opt_videopll.h" 48#ifdef VIDEOPLL_DEBUG 49#define DPRINTF printf 50#else 51#define DPRINTF while (0) printf 52#endif 53 54struct videopll_softc { 55 device_t sc_dev; 56 i2c_tag_t sc_tag; 57 int sc_addr; 58}; 59 60static int videopll_match(device_t, cfdata_t, void *); 61static void videopll_attach(device_t, device_t, void *); 62 63CFATTACH_DECL_NEW(videopll, sizeof(struct videopll_softc), 64 videopll_match, videopll_attach, NULL, NULL); 65 66static void *glob = NULL; 67 68static int 69videopll_match(device_t parent, cfdata_t cfdata, void *aux) 70{ 71 struct i2c_attach_args *ia = aux; 72 int match_result; 73 74 if (iic_use_direct_match(ia, cfdata, NULL, &match_result)) 75 return match_result; 76 77 /* This driver is direct-config only. */ 78 79 return 0; 80} 81 82static void 83videopll_attach(device_t parent, device_t self, void *aux) 84{ 85 struct i2c_attach_args *ia = aux; 86 struct videopll_softc *sc = device_private(self); 87 88 sc->sc_dev = self; 89 sc->sc_tag = ia->ia_tag; 90 sc->sc_addr = ia->ia_addr; 91 aprint_normal(": Apple onboard video PLL\n"); 92 glob = sc; 93} 94 95/* 96 * pixel clock: 97 * 3.9064MHz * 2^p2 * p1 / p0 98 */ 99int 100videopll_set_freq(int freq) 101{ 102 struct videopll_softc *sc = glob; 103 int p0, p1, p2; 104 int freq_out, diff, diff_b = 100000000; 105 int b0 = 0, b1 = 0, b2 = 0, freq_b = 0; 106 uint8_t cmdbuf[4]; 107 108 if (glob == NULL) 109 return EIO; 110 /* 111 * XXX 112 * The parameter ranges were taken from Linux' valkyriefb.c mode list. 113 * We don't really know what exact parameters the PLL supports but 114 * this should be enough for the modes we can actually support 115 */ 116 for (p2 = 2; p2 < 4; p2++) { 117 for (p1 = 27; p1 < 43; p1++) { 118 for (p0 = 11; p0 < 26; p0++) { 119 freq_out = (3906400 * (1 << p2) * p1) / (p0 * 1000); 120 diff = abs(freq - freq_out); 121 if (diff < diff_b) { 122 diff_b = diff; 123 b0 = p0; 124 b1 = p1; 125 b2 = p2; 126 freq_b = freq_out; 127 } 128 } 129 } 130 } 131 if (freq_b == 0) 132 return EINVAL; 133 DPRINTF("param: %d %d %d -> %d\n", b0, b1, b2, freq_b); 134 iic_acquire_bus(sc->sc_tag, 0); 135 cmdbuf[0] = 1; 136 cmdbuf[1] = b0; 137 iic_exec(sc->sc_tag, I2C_OP_WRITE, 138 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 139 cmdbuf[0] = 2; 140 cmdbuf[1] = b1; 141 iic_exec(sc->sc_tag, I2C_OP_WRITE, 142 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 143 cmdbuf[0] = 3; 144 cmdbuf[1] = b2; 145 iic_exec(sc->sc_tag, I2C_OP_WRITE, 146 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 147 iic_release_bus(sc->sc_tag, 0); 148 return 0; 149} 150 151 152 153