xref: /netbsd-src/sys/arch/evbppc/wii/dev/avenc.c (revision f5dff4bda8e057e10042cff439883c0c98a6e97b)
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, &reg, 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, &reg, 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, &reg, 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, &reg, 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