videopll.c revision 1.2
1/* $NetBSD: videopll.c,v 1.2 2017/09/22 04:01:41 macallan 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.2 2017/09/22 04:01:41 macallan 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 55struct videopll_softc { 56 device_t sc_dev; 57 i2c_tag_t sc_tag; 58 int sc_addr; 59}; 60 61static int videopll_match(device_t, cfdata_t, void *); 62static void videopll_attach(device_t, device_t, void *); 63 64CFATTACH_DECL_NEW(videopll, sizeof(struct videopll_softc), 65 videopll_match, videopll_attach, NULL, NULL); 66 67static void *glob = NULL; 68 69static int 70videopll_match(device_t parent, cfdata_t cfdata, void *aux) 71{ 72 struct i2c_attach_args *ia = aux; 73 74 if (strcmp(ia->ia_name, "videopll") == 0) 75 return 100; 76 77 return 0; 78} 79 80static void 81videopll_attach(device_t parent, device_t self, void *aux) 82{ 83 struct i2c_attach_args *ia = aux; 84 struct videopll_softc *sc = device_private(self); 85 86 sc->sc_dev = self; 87 sc->sc_tag = ia->ia_tag; 88 sc->sc_addr = ia->ia_addr; 89 aprint_normal(": Apple onboard video PLL\n"); 90 glob = sc; 91} 92 93/* 94 * pixel clock: 95 * 3.9064MHz * 2^p2 * p1 / p0 96 */ 97int 98videopll_set_freq(int freq) 99{ 100 struct videopll_softc *sc = glob; 101 int p0, p1, p2; 102 int freq_out, diff, diff_b = 100000000; 103 int b0 = 0, b1 = 0, b2 = 0, freq_b = 0; 104 uint8_t cmdbuf[4]; 105 106 if (glob == NULL) 107 return EIO; 108 /* 109 * XXX 110 * The parameter ranges were taken from Linux' valkyriefb.c mode list. 111 * We don't really know what exact parameters the PLL supports but 112 * this should be enough for the modes we can actually support 113 */ 114 for (p2 = 2; p2 < 4; p2++) { 115 for (p1 = 27; p1 < 43; p1++) { 116 for (p0 = 11; p0 < 26; p0++) { 117 freq_out = (3906400 * (1 << p2) * p1) / (p0 * 1000); 118 diff = abs(freq - freq_out); 119 if (diff < diff_b) { 120 diff_b = diff; 121 b0 = p0; 122 b1 = p1; 123 b2 = p2; 124 freq_b = freq_out; 125 } 126 } 127 } 128 } 129 if (freq_b == 0) 130 return EINVAL; 131 DPRINTF("param: %d %d %d -> %d\n", b0, b1, b2, freq_b); 132 iic_acquire_bus(sc->sc_tag, 0); 133 cmdbuf[0] = 1; 134 cmdbuf[1] = b0; 135 iic_exec(sc->sc_tag, I2C_OP_WRITE, 136 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 137 cmdbuf[0] = 2; 138 cmdbuf[1] = b1; 139 iic_exec(sc->sc_tag, I2C_OP_WRITE, 140 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 141 cmdbuf[0] = 3; 142 cmdbuf[1] = b2; 143 iic_exec(sc->sc_tag, I2C_OP_WRITE, 144 sc->sc_addr, cmdbuf, 2, NULL, 0, 0); 145 iic_release_bus(sc->sc_tag, 0); 146 return 0; 147} 148 149 150 151