11.1Sjmcneill/* $NetBSD: avenc.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 171.1Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 181.1Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 191.1Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 201.1Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 211.1Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 221.1Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 231.1Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 241.1Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 251.1Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 261.1Sjmcneill * POSSIBILITY OF SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: avenc.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/systm.h> 341.1Sjmcneill#include <sys/kernel.h> 351.1Sjmcneill#include <sys/device.h> 361.1Sjmcneill#include <sys/conf.h> 371.1Sjmcneill#include <sys/bus.h> 381.1Sjmcneill#include <sys/kmem.h> 391.1Sjmcneill#include <machine/wii.h> 401.1Sjmcneill#include <machine/wiiu.h> 411.1Sjmcneill 421.1Sjmcneill#include <lib/libkern/libkern.h> /* hexdump */ 431.1Sjmcneill 441.1Sjmcneill#include <dev/i2c/i2cvar.h> 451.1Sjmcneill 461.1Sjmcneill#include "avenc.h" 471.1Sjmcneill#include "vireg.h" 481.1Sjmcneill 491.1Sjmcneill#define AVENC_DEBUG 0 501.1Sjmcneill 511.1Sjmcneill#define AVENC_ADDR 0x70 521.1Sjmcneill 531.1Sjmcneill#define AVENC_VOLUME 0x71 541.1Sjmcneill#define AVENC_VOLUME_RIGHT __BITS(15,8) 551.1Sjmcneill#define AVENC_VOLUME_LEFT __BITS(7,0) 561.1Sjmcneill 571.1Sjmcneill#define RD1 avenc_read_1 581.1Sjmcneill#define RD2 avenc_read_2 591.1Sjmcneill#define WR1 avenc_write_1 601.1Sjmcneill#define WR2 avenc_write_2 611.1Sjmcneill#define WR4 avenc_write_4 621.1Sjmcneill#define WRBUF avenc_write_buf 631.1Sjmcneill 641.1Sjmcneillstatic i2c_tag_t avenc_tag; 651.1Sjmcneillstatic i2c_addr_t avenc_addr; 661.1Sjmcneill 671.1Sjmcneillstatic uint8_t 681.1Sjmcneillavenc_read_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 691.1Sjmcneill{ 701.1Sjmcneill uint8_t val; 711.1Sjmcneill 721.1Sjmcneill if (iic_smbus_read_byte(tag, addr, reg, &val, 0) != 0) { 731.1Sjmcneill val = 0xff; 741.1Sjmcneill } 751.1Sjmcneill 761.1Sjmcneill return val; 771.1Sjmcneill} 781.1Sjmcneill 791.1Sjmcneillstatic uint16_t 801.1Sjmcneillavenc_read_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg) 811.1Sjmcneill{ 821.1Sjmcneill uint8_t vbuf[2]; 831.1Sjmcneill 841.1Sjmcneill if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1, 851.1Sjmcneill vbuf, sizeof(vbuf), 0) != 0) { 861.1Sjmcneill return 0xffff; 871.1Sjmcneill } else { 881.1Sjmcneill return ((uint16_t)vbuf[0] << 8) | vbuf[1]; 891.1Sjmcneill } 901.1Sjmcneill} 911.1Sjmcneill 921.1Sjmcneillstatic void 931.1Sjmcneillavenc_write_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t val) 941.1Sjmcneill{ 951.1Sjmcneill iic_smbus_write_byte(tag, addr, reg, val, 0); 961.1Sjmcneill} 971.1Sjmcneill 981.1Sjmcneillstatic void 991.1Sjmcneillavenc_write_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint16_t val) 1001.1Sjmcneill{ 1011.1Sjmcneill uint8_t vbuf[2]; 1021.1Sjmcneill 1031.1Sjmcneill vbuf[0] = (val >> 8) & 0xff; 1041.1Sjmcneill vbuf[1] = val & 0xff; 1051.1Sjmcneill 1061.1Sjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 1071.1Sjmcneill vbuf, sizeof(vbuf), 0); 1081.1Sjmcneill} 1091.1Sjmcneill 1101.1Sjmcneillstatic void 1111.1Sjmcneillavenc_write_4(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint32_t val) 1121.1Sjmcneill{ 1131.1Sjmcneill uint8_t vbuf[4]; 1141.1Sjmcneill 1151.1Sjmcneill vbuf[0] = (val >> 24) & 0xff; 1161.1Sjmcneill vbuf[1] = (val >> 16) & 0xff; 1171.1Sjmcneill vbuf[2] = (val >> 8) & 0xff; 1181.1Sjmcneill vbuf[3] = val & 0xff; 1191.1Sjmcneill 1201.1Sjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 1211.1Sjmcneill vbuf, sizeof(vbuf), 0); 1221.1Sjmcneill} 1231.1Sjmcneill 1241.1Sjmcneillstatic void 1251.1Sjmcneillavenc_write_buf(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *buf, 1261.1Sjmcneill size_t buflen) 1271.1Sjmcneill{ 1281.1Sjmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1, 1291.1Sjmcneill buf, buflen, 0); 1301.1Sjmcneill} 1311.1Sjmcneill 1321.1Sjmcneillvoid 1331.1Sjmcneillavenc_get_volume(uint8_t *left, uint8_t *right) 1341.1Sjmcneill{ 1351.1Sjmcneill uint16_t val; 1361.1Sjmcneill 1371.1Sjmcneill if (avenc_addr == 0) { 1381.1Sjmcneill *left = *right = 0; 1391.1Sjmcneill return; 1401.1Sjmcneill } 1411.1Sjmcneill 1421.1Sjmcneill iic_acquire_bus(avenc_tag, 0); 1431.1Sjmcneill val = RD2(avenc_tag, avenc_addr, AVENC_VOLUME); 1441.1Sjmcneill iic_release_bus(avenc_tag, 0); 1451.1Sjmcneill 1461.1Sjmcneill *left = __SHIFTOUT(val, AVENC_VOLUME_LEFT); 1471.1Sjmcneill *right = __SHIFTOUT(val, AVENC_VOLUME_RIGHT); 1481.1Sjmcneill} 1491.1Sjmcneill 1501.1Sjmcneillvoid 1511.1Sjmcneillavenc_set_volume(uint8_t left, uint8_t right) 1521.1Sjmcneill{ 1531.1Sjmcneill uint16_t val; 1541.1Sjmcneill 1551.1Sjmcneill if (avenc_addr == 0) { 1561.1Sjmcneill return; 1571.1Sjmcneill } 1581.1Sjmcneill 1591.1Sjmcneill val = __SHIFTIN(left, AVENC_VOLUME_LEFT) | 1601.1Sjmcneill __SHIFTIN(right, AVENC_VOLUME_RIGHT); 1611.1Sjmcneill 1621.1Sjmcneill iic_acquire_bus(avenc_tag, 0); 1631.1Sjmcneill WR2(avenc_tag, avenc_addr, AVENC_VOLUME, val); 1641.1Sjmcneill iic_release_bus(avenc_tag, 0); 1651.1Sjmcneill} 1661.1Sjmcneill 1671.1Sjmcneillstatic void 1681.1Sjmcneillavenc_dump_regs(const char *pfx, i2c_tag_t tag, i2c_addr_t addr) 1691.1Sjmcneill{ 1701.1Sjmcneill uint8_t regdump[0x100]; 1711.1Sjmcneill unsigned int reg; 1721.1Sjmcneill 1731.1Sjmcneill iic_acquire_bus(tag, 0); 1741.1Sjmcneill 1751.1Sjmcneill for (reg = 0; reg < sizeof(regdump); reg++) { 1761.1Sjmcneill regdump[reg] = RD1(tag, addr, reg); 1771.1Sjmcneill } 1781.1Sjmcneill 1791.1Sjmcneill iic_release_bus(tag, 0); 1801.1Sjmcneill 1811.1Sjmcneill hexdump(printf, pfx, regdump, sizeof(regdump)); 1821.1Sjmcneill} 1831.1Sjmcneill 1841.1Sjmcneillstatic void 1851.1Sjmcneillavenc_init(i2c_tag_t tag, i2c_addr_t addr) 1861.1Sjmcneill{ 1871.1Sjmcneill uint8_t mvinit[26] = {}; 1881.1Sjmcneill uint8_t ginit[] = { 1891.1Sjmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 1901.1Sjmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60, 1911.1Sjmcneill 0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40, 1921.1Sjmcneill 0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb, 1931.1Sjmcneill 0x00 1941.1Sjmcneill }; 1951.1Sjmcneill uint8_t video_fmt; 1961.1Sjmcneill 1971.1Sjmcneill video_fmt = 0; 1981.1Sjmcneill if (__SHIFTOUT(in16(VI_BASE + VI_DCR), VI_DCR_FMT) == VI_DCR_FMT_PAL) { 1991.1Sjmcneill video_fmt |= 0x02; 2001.1Sjmcneill } 2011.1Sjmcneill if ((in16(VI_BASE + VI_VISEL) & VI_VISEL_COMPONENT_CABLE) != 0) { 2021.1Sjmcneill video_fmt |= 0x20; 2031.1Sjmcneill } 2041.1Sjmcneill 2051.1Sjmcneill iic_acquire_bus(tag, 0); 2061.1Sjmcneill WR1(tag, addr, 0x6a, 1); 2071.1Sjmcneill WR1(tag, addr, 0x65, 3); 2081.1Sjmcneill WR1(tag, addr, 0x01, video_fmt); 2091.1Sjmcneill WR1(tag, addr, 0x00, 0); 2101.1Sjmcneill WR1(tag, addr, 0x02, 7); 2111.1Sjmcneill WR2(tag, addr, 0x05, 0); 2121.1Sjmcneill WR2(tag, addr, 0x08, 0); 2131.1Sjmcneill WR4(tag, addr, 0x7a, 0); 2141.1Sjmcneill WRBUF(tag, addr, 0x40, mvinit, sizeof(mvinit)); 2151.1Sjmcneill WR1(tag, addr, 0x0a, 0); 2161.1Sjmcneill WR1(tag, addr, 0x03, 1); 2171.1Sjmcneill WRBUF(tag, addr, 0x10, ginit, sizeof(ginit)); 2181.1Sjmcneill WR1(tag, addr, 0x04, 1); 2191.1Sjmcneill WR4(tag, addr, 0x7a, 0); 2201.1Sjmcneill WR2(tag, addr, 0x08, 0); 2211.1Sjmcneill WR1(tag, addr, 0x03, 1); 2221.1Sjmcneill WR1(tag, addr, 0x6e, 0); 2231.1Sjmcneill iic_release_bus(tag, 0); 2241.1Sjmcneill 2251.1Sjmcneill avenc_set_volume(0x8e, 0x8e); 2261.1Sjmcneill} 2271.1Sjmcneill 2281.1Sjmcneill 2291.1Sjmcneillstatic int 2301.1Sjmcneillavenc_match(device_t parent, cfdata_t match, void *aux) 2311.1Sjmcneill{ 2321.1Sjmcneill struct i2c_attach_args *ia = aux; 2331.1Sjmcneill 2341.1Sjmcneill return !wiiu_plat && ia->ia_addr == AVENC_ADDR; 2351.1Sjmcneill} 2361.1Sjmcneill 2371.1Sjmcneillstatic void 2381.1Sjmcneillavenc_attach(device_t parent, device_t self, void *aux) 2391.1Sjmcneill{ 2401.1Sjmcneill struct i2c_attach_args *ia = aux; 2411.1Sjmcneill i2c_tag_t tag = ia->ia_tag; 2421.1Sjmcneill i2c_addr_t addr = ia->ia_addr; 2431.1Sjmcneill 2441.1Sjmcneill KASSERT(device_unit(self) == 0); 2451.1Sjmcneill 2461.1Sjmcneill aprint_naive("\n"); 2471.1Sjmcneill aprint_normal(": A/V Encoder\n"); 2481.1Sjmcneill 2491.1Sjmcneill if (AVENC_DEBUG) { 2501.1Sjmcneill avenc_dump_regs("avenc pre ", tag, addr); 2511.1Sjmcneill } 2521.1Sjmcneill 2531.1Sjmcneill avenc_tag = tag; 2541.1Sjmcneill avenc_addr = addr; 2551.1Sjmcneill 2561.1Sjmcneill avenc_init(tag, addr); 2571.1Sjmcneill 2581.1Sjmcneill if (AVENC_DEBUG) { 2591.1Sjmcneill avenc_dump_regs("avenc post", tag, addr); 2601.1Sjmcneill } 2611.1Sjmcneill} 2621.1Sjmcneill 2631.1SjmcneillCFATTACH_DECL_NEW(avenc, 0, avenc_match, avenc_attach, NULL, NULL); 264