1 /* $NetBSD: videopll.c,v 1.3 2018/06/16 21:22:13 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.3 2018/06/16 21:22:13 thorpej Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/device.h> 42 #include <sys/malloc.h> 43 44 #include <dev/ofw/openfirm.h> 45 #include <dev/i2c/i2cvar.h> 46 #include <arch/macppc/dev/videopllvar.h> 47 48 #include "opt_videopll.h" 49 #ifdef VIDEOPLL_DEBUG 50 #define DPRINTF printf 51 #else 52 #define DPRINTF while (0) printf 53 #endif 54 55 struct videopll_softc { 56 device_t sc_dev; 57 i2c_tag_t sc_tag; 58 int sc_addr; 59 }; 60 61 static int videopll_match(device_t, cfdata_t, void *); 62 static void videopll_attach(device_t, device_t, void *); 63 64 CFATTACH_DECL_NEW(videopll, sizeof(struct videopll_softc), 65 videopll_match, videopll_attach, NULL, NULL); 66 67 static void *glob = NULL; 68 69 static int 70 videopll_match(device_t parent, cfdata_t cfdata, void *aux) 71 { 72 struct i2c_attach_args *ia = aux; 73 int match_result; 74 75 if (iic_use_direct_match(ia, cfdata, NULL, &match_result)) 76 return match_result; 77 78 /* This driver is direct-config only. */ 79 80 return 0; 81 } 82 83 static void 84 videopll_attach(device_t parent, device_t self, void *aux) 85 { 86 struct i2c_attach_args *ia = aux; 87 struct videopll_softc *sc = device_private(self); 88 89 sc->sc_dev = self; 90 sc->sc_tag = ia->ia_tag; 91 sc->sc_addr = ia->ia_addr; 92 aprint_normal(": Apple onboard video PLL\n"); 93 glob = sc; 94 } 95 96 /* 97 * pixel clock: 98 * 3.9064MHz * 2^p2 * p1 / p0 99 */ 100 int 101 videopll_set_freq(int freq) 102 { 103 struct videopll_softc *sc = glob; 104 int p0, p1, p2; 105 int freq_out, diff, diff_b = 100000000; 106 int b0 = 0, b1 = 0, b2 = 0, freq_b = 0; 107 uint8_t cmdbuf[4]; 108 109 if (glob == NULL) 110 return EIO; 111 /* 112 * XXX 113 * The parameter ranges were taken from Linux' valkyriefb.c mode list. 114 * We don't really know what exact parameters the PLL supports but 115 * this should be enough for the modes we can actually support 116 */ 117 for (p2 = 2; p2 < 4; p2++) { 118 for (p1 = 27; p1 < 43; p1++) { 119 for (p0 = 11; p0 < 26; p0++) { 120 freq_out = (3906400 * (1 << p2) * p1) / (p0 * 1000); 121 diff = abs(freq - freq_out); 122 if (diff < diff_b) { 123 diff_b = diff; 124 b0 = p0; 125 b1 = p1; 126 b2 = p2; 127 freq_b = freq_out; 128 } 129 } 130 } 131 } 132 if (freq_b == 0) 133 return EINVAL; 134 DPRINTF("param: %d %d %d -> %d\n", b0, b1, b2, freq_b); 135 iic_acquire_bus(sc->sc_tag, 0); 136 cmdbuf[0] = 1; 137 cmdbuf[1] = b0; 138 iic_exec(sc->sc_tag, I2C_OP_WRITE, 139 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 140 cmdbuf[0] = 2; 141 cmdbuf[1] = b1; 142 iic_exec(sc->sc_tag, I2C_OP_WRITE, 143 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 144 cmdbuf[0] = 3; 145 cmdbuf[1] = b2; 146 iic_exec(sc->sc_tag, I2C_OP_WRITE, 147 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 148 iic_release_bus(sc->sc_tag, 0); 149 return 0; 150 } 151 152 153 154