avenc.c revision 1.1
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, &reg, 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, &reg, 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, &reg, 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, &reg, 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