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