1*f5dff4bdSjmcneill /* $NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $ */ 2297a048cSjmcneill 3297a048cSjmcneill /*- 4297a048cSjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> 5297a048cSjmcneill * All rights reserved. 6297a048cSjmcneill * 7297a048cSjmcneill * Redistribution and use in source and binary forms, with or without 8297a048cSjmcneill * modification, are permitted provided that the following conditions 9297a048cSjmcneill * are met: 10297a048cSjmcneill * 1. Redistributions of source code must retain the above copyright 11297a048cSjmcneill * notice, this list of conditions and the following disclaimer. 12297a048cSjmcneill * 2. Redistributions in binary form must reproduce the above copyright 13297a048cSjmcneill * notice, this list of conditions and the following disclaimer in the 14297a048cSjmcneill * documentation and/or other materials provided with the distribution. 15297a048cSjmcneill * 16297a048cSjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17297a048cSjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18297a048cSjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19297a048cSjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20297a048cSjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21297a048cSjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22297a048cSjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23297a048cSjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24297a048cSjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25297a048cSjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26297a048cSjmcneill * POSSIBILITY OF SUCH DAMAGE. 27297a048cSjmcneill */ 28297a048cSjmcneill 29297a048cSjmcneill #include <sys/cdefs.h> 30*f5dff4bdSjmcneill __KERNEL_RCSID(0, "$NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $"); 31297a048cSjmcneill 32297a048cSjmcneill #include <sys/param.h> 33297a048cSjmcneill #include <sys/systm.h> 34297a048cSjmcneill #include <sys/kernel.h> 35297a048cSjmcneill #include <sys/device.h> 36297a048cSjmcneill #include <sys/conf.h> 37297a048cSjmcneill #include <sys/bus.h> 38297a048cSjmcneill #include <sys/kmem.h> 39*f5dff4bdSjmcneill #include <machine/wii.h> 40297a048cSjmcneill 41297a048cSjmcneill #include <lib/libkern/libkern.h> /* hexdump */ 42297a048cSjmcneill 43297a048cSjmcneill #include <dev/i2c/i2cvar.h> 44297a048cSjmcneill 45297a048cSjmcneill #include "avenc.h" 46*f5dff4bdSjmcneill #include "vireg.h" 47297a048cSjmcneill 48297a048cSjmcneill #define AVENC_DEBUG 0 49297a048cSjmcneill 50297a048cSjmcneill #define AVENC_ADDR 0x70 51297a048cSjmcneill 52297a048cSjmcneill #define AVENC_VOLUME 0x71 53297a048cSjmcneill #define AVENC_VOLUME_RIGHT __BITS(15,8) 54297a048cSjmcneill #define AVENC_VOLUME_LEFT __BITS(7,0) 55297a048cSjmcneill 56297a048cSjmcneill #define RD1 avenc_read_1 57297a048cSjmcneill #define RD2 avenc_read_2 58*f5dff4bdSjmcneill #define WR1 avenc_write_1 59297a048cSjmcneill #define WR2 avenc_write_2 60*f5dff4bdSjmcneill #define WR4 avenc_write_4 61*f5dff4bdSjmcneill #define WRBUF avenc_write_buf 62297a048cSjmcneill 63297a048cSjmcneill static i2c_tag_t avenc_tag; 64297a048cSjmcneill static i2c_addr_t avenc_addr; 65297a048cSjmcneill 66297a048cSjmcneill static uint8_t 67297a048cSjmcneill avenc_read_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 68297a048cSjmcneill { 69297a048cSjmcneill uint8_t val; 70297a048cSjmcneill 71297a048cSjmcneill if (iic_smbus_read_byte(tag, addr, reg, &val, 0) != 0) { 72297a048cSjmcneill val = 0xff; 73297a048cSjmcneill } 74297a048cSjmcneill 75297a048cSjmcneill return val; 76297a048cSjmcneill } 77297a048cSjmcneill 78297a048cSjmcneill static uint16_t 79297a048cSjmcneill avenc_read_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 80297a048cSjmcneill { 81*f5dff4bdSjmcneill uint8_t vbuf[2]; 82297a048cSjmcneill 83*f5dff4bdSjmcneill if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1, 84*f5dff4bdSjmcneill vbuf, sizeof(vbuf), 0) != 0) { 85*f5dff4bdSjmcneill return 0xffff; 86*f5dff4bdSjmcneill } else { 87*f5dff4bdSjmcneill return ((uint16_t)vbuf[0] << 8) | vbuf[1]; 88*f5dff4bdSjmcneill } 89297a048cSjmcneill } 90297a048cSjmcneill 91*f5dff4bdSjmcneill static void 92*f5dff4bdSjmcneill avenc_write_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t val) 93*f5dff4bdSjmcneill { 94*f5dff4bdSjmcneill iic_smbus_write_byte(tag, addr, reg, val, 0); 95297a048cSjmcneill } 96297a048cSjmcneill 97297a048cSjmcneill static void 98297a048cSjmcneill avenc_write_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint16_t val) 99297a048cSjmcneill { 100*f5dff4bdSjmcneill uint8_t vbuf[2]; 101*f5dff4bdSjmcneill 102*f5dff4bdSjmcneill vbuf[0] = (val >> 8) & 0xff; 103*f5dff4bdSjmcneill vbuf[1] = val & 0xff; 104*f5dff4bdSjmcneill 105*f5dff4bdSjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 106*f5dff4bdSjmcneill vbuf, sizeof(vbuf), 0); 107*f5dff4bdSjmcneill } 108*f5dff4bdSjmcneill 109*f5dff4bdSjmcneill static void 110*f5dff4bdSjmcneill avenc_write_4(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint32_t val) 111*f5dff4bdSjmcneill { 112*f5dff4bdSjmcneill uint8_t vbuf[4]; 113*f5dff4bdSjmcneill 114*f5dff4bdSjmcneill vbuf[0] = (val >> 24) & 0xff; 115*f5dff4bdSjmcneill vbuf[1] = (val >> 16) & 0xff; 116*f5dff4bdSjmcneill vbuf[2] = (val >> 8) & 0xff; 117*f5dff4bdSjmcneill vbuf[3] = val & 0xff; 118*f5dff4bdSjmcneill 119*f5dff4bdSjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 120*f5dff4bdSjmcneill vbuf, sizeof(vbuf), 0); 121*f5dff4bdSjmcneill } 122*f5dff4bdSjmcneill 123*f5dff4bdSjmcneill static void 124*f5dff4bdSjmcneill avenc_write_buf(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *buf, 125*f5dff4bdSjmcneill size_t buflen) 126*f5dff4bdSjmcneill { 127*f5dff4bdSjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 128*f5dff4bdSjmcneill buf, buflen, 0); 129297a048cSjmcneill } 130297a048cSjmcneill 131297a048cSjmcneill void 132297a048cSjmcneill avenc_get_volume(uint8_t *left, uint8_t *right) 133297a048cSjmcneill { 134297a048cSjmcneill uint16_t val; 135297a048cSjmcneill 136297a048cSjmcneill if (avenc_addr == 0) { 137297a048cSjmcneill *left = *right = 0; 138297a048cSjmcneill return; 139297a048cSjmcneill } 140297a048cSjmcneill 141297a048cSjmcneill iic_acquire_bus(avenc_tag, 0); 142297a048cSjmcneill val = RD2(avenc_tag, avenc_addr, AVENC_VOLUME); 143297a048cSjmcneill iic_release_bus(avenc_tag, 0); 144297a048cSjmcneill 145297a048cSjmcneill *left = __SHIFTOUT(val, AVENC_VOLUME_LEFT); 146297a048cSjmcneill *right = __SHIFTOUT(val, AVENC_VOLUME_RIGHT); 147297a048cSjmcneill } 148297a048cSjmcneill 149297a048cSjmcneill void 150297a048cSjmcneill avenc_set_volume(uint8_t left, uint8_t right) 151297a048cSjmcneill { 152297a048cSjmcneill uint16_t val; 153297a048cSjmcneill 154297a048cSjmcneill if (avenc_addr == 0) { 155297a048cSjmcneill return; 156297a048cSjmcneill } 157297a048cSjmcneill 158297a048cSjmcneill val = __SHIFTIN(left, AVENC_VOLUME_LEFT) | 159297a048cSjmcneill __SHIFTIN(right, AVENC_VOLUME_RIGHT); 160297a048cSjmcneill 161297a048cSjmcneill iic_acquire_bus(avenc_tag, 0); 162297a048cSjmcneill WR2(avenc_tag, avenc_addr, AVENC_VOLUME, val); 163297a048cSjmcneill iic_release_bus(avenc_tag, 0); 164297a048cSjmcneill } 165297a048cSjmcneill 166297a048cSjmcneill static void 167*f5dff4bdSjmcneill avenc_dump_regs(const char *pfx, i2c_tag_t tag, i2c_addr_t addr) 168297a048cSjmcneill { 169297a048cSjmcneill uint8_t regdump[0x100]; 170297a048cSjmcneill unsigned int reg; 171297a048cSjmcneill 172297a048cSjmcneill iic_acquire_bus(tag, 0); 173297a048cSjmcneill 174297a048cSjmcneill for (reg = 0; reg < sizeof(regdump); reg++) { 175297a048cSjmcneill regdump[reg] = RD1(tag, addr, reg); 176297a048cSjmcneill } 177297a048cSjmcneill 178297a048cSjmcneill iic_release_bus(tag, 0); 179297a048cSjmcneill 180*f5dff4bdSjmcneill hexdump(printf, pfx, regdump, sizeof(regdump)); 181297a048cSjmcneill } 182297a048cSjmcneill 183*f5dff4bdSjmcneill static void 184*f5dff4bdSjmcneill avenc_init(i2c_tag_t tag, i2c_addr_t addr) 185*f5dff4bdSjmcneill { 186*f5dff4bdSjmcneill uint8_t mvinit[26] = {}; 187*f5dff4bdSjmcneill uint8_t ginit[] = { 188*f5dff4bdSjmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 189*f5dff4bdSjmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60, 190*f5dff4bdSjmcneill 0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40, 191*f5dff4bdSjmcneill 0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb, 192*f5dff4bdSjmcneill 0x00 193*f5dff4bdSjmcneill }; 194*f5dff4bdSjmcneill uint8_t video_fmt; 195*f5dff4bdSjmcneill 196*f5dff4bdSjmcneill video_fmt = 0; 197*f5dff4bdSjmcneill if (__SHIFTOUT(in16(VI_BASE + VI_DCR), VI_DCR_FMT) == VI_DCR_FMT_PAL) { 198*f5dff4bdSjmcneill video_fmt |= 0x02; 199*f5dff4bdSjmcneill } 200*f5dff4bdSjmcneill if ((in16(VI_BASE + VI_VISEL) & VI_VISEL_COMPONENT_CABLE) != 0) { 201*f5dff4bdSjmcneill video_fmt |= 0x20; 202*f5dff4bdSjmcneill } 203*f5dff4bdSjmcneill 204*f5dff4bdSjmcneill iic_acquire_bus(tag, 0); 205*f5dff4bdSjmcneill WR1(tag, addr, 0x6a, 1); 206*f5dff4bdSjmcneill WR1(tag, addr, 0x65, 3); 207*f5dff4bdSjmcneill WR1(tag, addr, 0x01, video_fmt); 208*f5dff4bdSjmcneill WR1(tag, addr, 0x00, 0); 209*f5dff4bdSjmcneill WR1(tag, addr, 0x02, 7); 210*f5dff4bdSjmcneill WR2(tag, addr, 0x05, 0); 211*f5dff4bdSjmcneill WR2(tag, addr, 0x08, 0); 212*f5dff4bdSjmcneill WR4(tag, addr, 0x7a, 0); 213*f5dff4bdSjmcneill WRBUF(tag, addr, 0x40, mvinit, sizeof(mvinit)); 214*f5dff4bdSjmcneill WR1(tag, addr, 0x0a, 0); 215*f5dff4bdSjmcneill WR1(tag, addr, 0x03, 1); 216*f5dff4bdSjmcneill WRBUF(tag, addr, 0x10, ginit, sizeof(ginit)); 217*f5dff4bdSjmcneill WR1(tag, addr, 0x04, 1); 218*f5dff4bdSjmcneill WR4(tag, addr, 0x7a, 0); 219*f5dff4bdSjmcneill WR2(tag, addr, 0x08, 0); 220*f5dff4bdSjmcneill WR1(tag, addr, 0x03, 1); 221*f5dff4bdSjmcneill WR1(tag, addr, 0x6e, 0); 222*f5dff4bdSjmcneill iic_release_bus(tag, 0); 223*f5dff4bdSjmcneill 224*f5dff4bdSjmcneill avenc_set_volume(0x8e, 0x8e); 225*f5dff4bdSjmcneill } 226*f5dff4bdSjmcneill 227*f5dff4bdSjmcneill 228297a048cSjmcneill static int 229297a048cSjmcneill avenc_match(device_t parent, cfdata_t match, void *aux) 230297a048cSjmcneill { 231297a048cSjmcneill struct i2c_attach_args *ia = aux; 232297a048cSjmcneill 233297a048cSjmcneill return ia->ia_addr == AVENC_ADDR; 234297a048cSjmcneill } 235297a048cSjmcneill 236297a048cSjmcneill static void 237297a048cSjmcneill avenc_attach(device_t parent, device_t self, void *aux) 238297a048cSjmcneill { 239297a048cSjmcneill struct i2c_attach_args *ia = aux; 240297a048cSjmcneill i2c_tag_t tag = ia->ia_tag; 241297a048cSjmcneill i2c_addr_t addr = ia->ia_addr; 242297a048cSjmcneill 243297a048cSjmcneill KASSERT(device_unit(self) == 0); 244297a048cSjmcneill 245297a048cSjmcneill aprint_naive("\n"); 246297a048cSjmcneill aprint_normal(": A/V Encoder\n"); 247297a048cSjmcneill 248297a048cSjmcneill if (AVENC_DEBUG) { 249*f5dff4bdSjmcneill avenc_dump_regs("avenc pre ", tag, addr); 250297a048cSjmcneill } 251297a048cSjmcneill 252297a048cSjmcneill avenc_tag = tag; 253297a048cSjmcneill avenc_addr = addr; 254*f5dff4bdSjmcneill 255*f5dff4bdSjmcneill avenc_init(tag, addr); 256*f5dff4bdSjmcneill 257*f5dff4bdSjmcneill if (AVENC_DEBUG) { 258*f5dff4bdSjmcneill avenc_dump_regs("avenc post", tag, addr); 259*f5dff4bdSjmcneill } 260297a048cSjmcneill } 261297a048cSjmcneill 262297a048cSjmcneill CFATTACH_DECL_NEW(avenc, 0, avenc_match, avenc_attach, NULL, NULL); 263