1 /* $NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/conf.h> 37 #include <sys/bus.h> 38 #include <sys/kmem.h> 39 #include <machine/wii.h> 40 41 #include <lib/libkern/libkern.h> /* hexdump */ 42 43 #include <dev/i2c/i2cvar.h> 44 45 #include "avenc.h" 46 #include "vireg.h" 47 48 #define AVENC_DEBUG 0 49 50 #define AVENC_ADDR 0x70 51 52 #define AVENC_VOLUME 0x71 53 #define AVENC_VOLUME_RIGHT __BITS(15,8) 54 #define AVENC_VOLUME_LEFT __BITS(7,0) 55 56 #define RD1 avenc_read_1 57 #define RD2 avenc_read_2 58 #define WR1 avenc_write_1 59 #define WR2 avenc_write_2 60 #define WR4 avenc_write_4 61 #define WRBUF avenc_write_buf 62 63 static i2c_tag_t avenc_tag; 64 static i2c_addr_t avenc_addr; 65 66 static uint8_t 67 avenc_read_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 68 { 69 uint8_t val; 70 71 if (iic_smbus_read_byte(tag, addr, reg, &val, 0) != 0) { 72 val = 0xff; 73 } 74 75 return val; 76 } 77 78 static uint16_t 79 avenc_read_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 80 { 81 uint8_t vbuf[2]; 82 83 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1, 84 vbuf, sizeof(vbuf), 0) != 0) { 85 return 0xffff; 86 } else { 87 return ((uint16_t)vbuf[0] << 8) | vbuf[1]; 88 } 89 } 90 91 static void 92 avenc_write_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t val) 93 { 94 iic_smbus_write_byte(tag, addr, reg, val, 0); 95 } 96 97 static void 98 avenc_write_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint16_t val) 99 { 100 uint8_t vbuf[2]; 101 102 vbuf[0] = (val >> 8) & 0xff; 103 vbuf[1] = val & 0xff; 104 105 iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 106 vbuf, sizeof(vbuf), 0); 107 } 108 109 static void 110 avenc_write_4(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint32_t val) 111 { 112 uint8_t vbuf[4]; 113 114 vbuf[0] = (val >> 24) & 0xff; 115 vbuf[1] = (val >> 16) & 0xff; 116 vbuf[2] = (val >> 8) & 0xff; 117 vbuf[3] = val & 0xff; 118 119 iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 120 vbuf, sizeof(vbuf), 0); 121 } 122 123 static void 124 avenc_write_buf(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *buf, 125 size_t buflen) 126 { 127 iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 128 buf, buflen, 0); 129 } 130 131 void 132 avenc_get_volume(uint8_t *left, uint8_t *right) 133 { 134 uint16_t val; 135 136 if (avenc_addr == 0) { 137 *left = *right = 0; 138 return; 139 } 140 141 iic_acquire_bus(avenc_tag, 0); 142 val = RD2(avenc_tag, avenc_addr, AVENC_VOLUME); 143 iic_release_bus(avenc_tag, 0); 144 145 *left = __SHIFTOUT(val, AVENC_VOLUME_LEFT); 146 *right = __SHIFTOUT(val, AVENC_VOLUME_RIGHT); 147 } 148 149 void 150 avenc_set_volume(uint8_t left, uint8_t right) 151 { 152 uint16_t val; 153 154 if (avenc_addr == 0) { 155 return; 156 } 157 158 val = __SHIFTIN(left, AVENC_VOLUME_LEFT) | 159 __SHIFTIN(right, AVENC_VOLUME_RIGHT); 160 161 iic_acquire_bus(avenc_tag, 0); 162 WR2(avenc_tag, avenc_addr, AVENC_VOLUME, val); 163 iic_release_bus(avenc_tag, 0); 164 } 165 166 static void 167 avenc_dump_regs(const char *pfx, i2c_tag_t tag, i2c_addr_t addr) 168 { 169 uint8_t regdump[0x100]; 170 unsigned int reg; 171 172 iic_acquire_bus(tag, 0); 173 174 for (reg = 0; reg < sizeof(regdump); reg++) { 175 regdump[reg] = RD1(tag, addr, reg); 176 } 177 178 iic_release_bus(tag, 0); 179 180 hexdump(printf, pfx, regdump, sizeof(regdump)); 181 } 182 183 static void 184 avenc_init(i2c_tag_t tag, i2c_addr_t addr) 185 { 186 uint8_t mvinit[26] = {}; 187 uint8_t ginit[] = { 188 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 189 0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60, 190 0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40, 191 0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb, 192 0x00 193 }; 194 uint8_t video_fmt; 195 196 video_fmt = 0; 197 if (__SHIFTOUT(in16(VI_BASE + VI_DCR), VI_DCR_FMT) == VI_DCR_FMT_PAL) { 198 video_fmt |= 0x02; 199 } 200 if ((in16(VI_BASE + VI_VISEL) & VI_VISEL_COMPONENT_CABLE) != 0) { 201 video_fmt |= 0x20; 202 } 203 204 iic_acquire_bus(tag, 0); 205 WR1(tag, addr, 0x6a, 1); 206 WR1(tag, addr, 0x65, 3); 207 WR1(tag, addr, 0x01, video_fmt); 208 WR1(tag, addr, 0x00, 0); 209 WR1(tag, addr, 0x02, 7); 210 WR2(tag, addr, 0x05, 0); 211 WR2(tag, addr, 0x08, 0); 212 WR4(tag, addr, 0x7a, 0); 213 WRBUF(tag, addr, 0x40, mvinit, sizeof(mvinit)); 214 WR1(tag, addr, 0x0a, 0); 215 WR1(tag, addr, 0x03, 1); 216 WRBUF(tag, addr, 0x10, ginit, sizeof(ginit)); 217 WR1(tag, addr, 0x04, 1); 218 WR4(tag, addr, 0x7a, 0); 219 WR2(tag, addr, 0x08, 0); 220 WR1(tag, addr, 0x03, 1); 221 WR1(tag, addr, 0x6e, 0); 222 iic_release_bus(tag, 0); 223 224 avenc_set_volume(0x8e, 0x8e); 225 } 226 227 228 static int 229 avenc_match(device_t parent, cfdata_t match, void *aux) 230 { 231 struct i2c_attach_args *ia = aux; 232 233 return ia->ia_addr == AVENC_ADDR; 234 } 235 236 static void 237 avenc_attach(device_t parent, device_t self, void *aux) 238 { 239 struct i2c_attach_args *ia = aux; 240 i2c_tag_t tag = ia->ia_tag; 241 i2c_addr_t addr = ia->ia_addr; 242 243 KASSERT(device_unit(self) == 0); 244 245 aprint_naive("\n"); 246 aprint_normal(": A/V Encoder\n"); 247 248 if (AVENC_DEBUG) { 249 avenc_dump_regs("avenc pre ", tag, addr); 250 } 251 252 avenc_tag = tag; 253 avenc_addr = addr; 254 255 avenc_init(tag, addr); 256 257 if (AVENC_DEBUG) { 258 avenc_dump_regs("avenc post", tag, addr); 259 } 260 } 261 262 CFATTACH_DECL_NEW(avenc, 0, avenc_match, avenc_attach, NULL, NULL); 263