avenc.c revision 1.2 1 /* $NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2024 Jared McNeill <jmcneill (at) 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, ®, 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, ®, 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, ®, 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, ®, 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