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