avenc.c revision 1.2 1 1.2 jmcneill /* $NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $ */
2 1.1 jmcneill
3 1.1 jmcneill /*-
4 1.1 jmcneill * Copyright (c) 2024 Jared McNeill <jmcneill (at) invisible.ca>
5 1.1 jmcneill * All rights reserved.
6 1.1 jmcneill *
7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without
8 1.1 jmcneill * modification, are permitted provided that the following conditions
9 1.1 jmcneill * are met:
10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright
11 1.1 jmcneill * notice, this list of conditions and the following disclaimer.
12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the
14 1.1 jmcneill * documentation and/or other materials provided with the distribution.
15 1.1 jmcneill *
16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE.
27 1.1 jmcneill */
28 1.1 jmcneill
29 1.1 jmcneill #include <sys/cdefs.h>
30 1.2 jmcneill __KERNEL_RCSID(0, "$NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $");
31 1.1 jmcneill
32 1.1 jmcneill #include <sys/param.h>
33 1.1 jmcneill #include <sys/systm.h>
34 1.1 jmcneill #include <sys/kernel.h>
35 1.1 jmcneill #include <sys/device.h>
36 1.1 jmcneill #include <sys/conf.h>
37 1.1 jmcneill #include <sys/bus.h>
38 1.1 jmcneill #include <sys/kmem.h>
39 1.2 jmcneill #include <machine/wii.h>
40 1.1 jmcneill
41 1.1 jmcneill #include <lib/libkern/libkern.h> /* hexdump */
42 1.1 jmcneill
43 1.1 jmcneill #include <dev/i2c/i2cvar.h>
44 1.1 jmcneill
45 1.1 jmcneill #include "avenc.h"
46 1.2 jmcneill #include "vireg.h"
47 1.1 jmcneill
48 1.1 jmcneill #define AVENC_DEBUG 0
49 1.1 jmcneill
50 1.1 jmcneill #define AVENC_ADDR 0x70
51 1.1 jmcneill
52 1.1 jmcneill #define AVENC_VOLUME 0x71
53 1.1 jmcneill #define AVENC_VOLUME_RIGHT __BITS(15,8)
54 1.1 jmcneill #define AVENC_VOLUME_LEFT __BITS(7,0)
55 1.1 jmcneill
56 1.1 jmcneill #define RD1 avenc_read_1
57 1.1 jmcneill #define RD2 avenc_read_2
58 1.2 jmcneill #define WR1 avenc_write_1
59 1.1 jmcneill #define WR2 avenc_write_2
60 1.2 jmcneill #define WR4 avenc_write_4
61 1.2 jmcneill #define WRBUF avenc_write_buf
62 1.1 jmcneill
63 1.1 jmcneill static i2c_tag_t avenc_tag;
64 1.1 jmcneill static i2c_addr_t avenc_addr;
65 1.1 jmcneill
66 1.1 jmcneill static uint8_t
67 1.1 jmcneill avenc_read_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg)
68 1.1 jmcneill {
69 1.1 jmcneill uint8_t val;
70 1.1 jmcneill
71 1.1 jmcneill if (iic_smbus_read_byte(tag, addr, reg, &val, 0) != 0) {
72 1.1 jmcneill val = 0xff;
73 1.1 jmcneill }
74 1.1 jmcneill
75 1.1 jmcneill return val;
76 1.1 jmcneill }
77 1.1 jmcneill
78 1.1 jmcneill static uint16_t
79 1.1 jmcneill avenc_read_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg)
80 1.1 jmcneill {
81 1.2 jmcneill uint8_t vbuf[2];
82 1.1 jmcneill
83 1.2 jmcneill if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1,
84 1.2 jmcneill vbuf, sizeof(vbuf), 0) != 0) {
85 1.2 jmcneill return 0xffff;
86 1.2 jmcneill } else {
87 1.2 jmcneill return ((uint16_t)vbuf[0] << 8) | vbuf[1];
88 1.1 jmcneill }
89 1.2 jmcneill }
90 1.1 jmcneill
91 1.2 jmcneill static void
92 1.2 jmcneill avenc_write_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t val)
93 1.2 jmcneill {
94 1.2 jmcneill iic_smbus_write_byte(tag, addr, reg, val, 0);
95 1.1 jmcneill }
96 1.1 jmcneill
97 1.1 jmcneill static void
98 1.1 jmcneill avenc_write_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint16_t val)
99 1.1 jmcneill {
100 1.2 jmcneill uint8_t vbuf[2];
101 1.2 jmcneill
102 1.2 jmcneill vbuf[0] = (val >> 8) & 0xff;
103 1.2 jmcneill vbuf[1] = val & 0xff;
104 1.2 jmcneill
105 1.2 jmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1,
106 1.2 jmcneill vbuf, sizeof(vbuf), 0);
107 1.2 jmcneill }
108 1.2 jmcneill
109 1.2 jmcneill static void
110 1.2 jmcneill avenc_write_4(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint32_t val)
111 1.2 jmcneill {
112 1.2 jmcneill uint8_t vbuf[4];
113 1.2 jmcneill
114 1.2 jmcneill vbuf[0] = (val >> 24) & 0xff;
115 1.2 jmcneill vbuf[1] = (val >> 16) & 0xff;
116 1.2 jmcneill vbuf[2] = (val >> 8) & 0xff;
117 1.2 jmcneill vbuf[3] = val & 0xff;
118 1.2 jmcneill
119 1.2 jmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1,
120 1.2 jmcneill vbuf, sizeof(vbuf), 0);
121 1.2 jmcneill }
122 1.2 jmcneill
123 1.2 jmcneill static void
124 1.2 jmcneill avenc_write_buf(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *buf,
125 1.2 jmcneill size_t buflen)
126 1.2 jmcneill {
127 1.2 jmcneill iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, ®, 1,
128 1.2 jmcneill buf, buflen, 0);
129 1.1 jmcneill }
130 1.1 jmcneill
131 1.1 jmcneill void
132 1.1 jmcneill avenc_get_volume(uint8_t *left, uint8_t *right)
133 1.1 jmcneill {
134 1.1 jmcneill uint16_t val;
135 1.1 jmcneill
136 1.1 jmcneill if (avenc_addr == 0) {
137 1.1 jmcneill *left = *right = 0;
138 1.1 jmcneill return;
139 1.1 jmcneill }
140 1.1 jmcneill
141 1.1 jmcneill iic_acquire_bus(avenc_tag, 0);
142 1.1 jmcneill val = RD2(avenc_tag, avenc_addr, AVENC_VOLUME);
143 1.1 jmcneill iic_release_bus(avenc_tag, 0);
144 1.1 jmcneill
145 1.1 jmcneill *left = __SHIFTOUT(val, AVENC_VOLUME_LEFT);
146 1.1 jmcneill *right = __SHIFTOUT(val, AVENC_VOLUME_RIGHT);
147 1.1 jmcneill }
148 1.1 jmcneill
149 1.1 jmcneill void
150 1.1 jmcneill avenc_set_volume(uint8_t left, uint8_t right)
151 1.1 jmcneill {
152 1.1 jmcneill uint16_t val;
153 1.1 jmcneill
154 1.1 jmcneill if (avenc_addr == 0) {
155 1.1 jmcneill return;
156 1.1 jmcneill }
157 1.1 jmcneill
158 1.1 jmcneill val = __SHIFTIN(left, AVENC_VOLUME_LEFT) |
159 1.1 jmcneill __SHIFTIN(right, AVENC_VOLUME_RIGHT);
160 1.1 jmcneill
161 1.1 jmcneill iic_acquire_bus(avenc_tag, 0);
162 1.1 jmcneill WR2(avenc_tag, avenc_addr, AVENC_VOLUME, val);
163 1.1 jmcneill iic_release_bus(avenc_tag, 0);
164 1.1 jmcneill }
165 1.1 jmcneill
166 1.1 jmcneill static void
167 1.2 jmcneill avenc_dump_regs(const char *pfx, i2c_tag_t tag, i2c_addr_t addr)
168 1.1 jmcneill {
169 1.1 jmcneill uint8_t regdump[0x100];
170 1.1 jmcneill unsigned int reg;
171 1.1 jmcneill
172 1.1 jmcneill iic_acquire_bus(tag, 0);
173 1.1 jmcneill
174 1.1 jmcneill for (reg = 0; reg < sizeof(regdump); reg++) {
175 1.1 jmcneill regdump[reg] = RD1(tag, addr, reg);
176 1.1 jmcneill }
177 1.1 jmcneill
178 1.1 jmcneill iic_release_bus(tag, 0);
179 1.1 jmcneill
180 1.2 jmcneill hexdump(printf, pfx, regdump, sizeof(regdump));
181 1.2 jmcneill }
182 1.2 jmcneill
183 1.2 jmcneill static void
184 1.2 jmcneill avenc_init(i2c_tag_t tag, i2c_addr_t addr)
185 1.2 jmcneill {
186 1.2 jmcneill uint8_t mvinit[26] = {};
187 1.2 jmcneill uint8_t ginit[] = {
188 1.2 jmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00,
189 1.2 jmcneill 0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60,
190 1.2 jmcneill 0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40,
191 1.2 jmcneill 0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb,
192 1.2 jmcneill 0x00
193 1.2 jmcneill };
194 1.2 jmcneill uint8_t video_fmt;
195 1.2 jmcneill
196 1.2 jmcneill video_fmt = 0;
197 1.2 jmcneill if (__SHIFTOUT(in16(VI_BASE + VI_DCR), VI_DCR_FMT) == VI_DCR_FMT_PAL) {
198 1.2 jmcneill video_fmt |= 0x02;
199 1.2 jmcneill }
200 1.2 jmcneill if ((in16(VI_BASE + VI_VISEL) & VI_VISEL_COMPONENT_CABLE) != 0) {
201 1.2 jmcneill video_fmt |= 0x20;
202 1.2 jmcneill }
203 1.2 jmcneill
204 1.2 jmcneill iic_acquire_bus(tag, 0);
205 1.2 jmcneill WR1(tag, addr, 0x6a, 1);
206 1.2 jmcneill WR1(tag, addr, 0x65, 3);
207 1.2 jmcneill WR1(tag, addr, 0x01, video_fmt);
208 1.2 jmcneill WR1(tag, addr, 0x00, 0);
209 1.2 jmcneill WR1(tag, addr, 0x02, 7);
210 1.2 jmcneill WR2(tag, addr, 0x05, 0);
211 1.2 jmcneill WR2(tag, addr, 0x08, 0);
212 1.2 jmcneill WR4(tag, addr, 0x7a, 0);
213 1.2 jmcneill WRBUF(tag, addr, 0x40, mvinit, sizeof(mvinit));
214 1.2 jmcneill WR1(tag, addr, 0x0a, 0);
215 1.2 jmcneill WR1(tag, addr, 0x03, 1);
216 1.2 jmcneill WRBUF(tag, addr, 0x10, ginit, sizeof(ginit));
217 1.2 jmcneill WR1(tag, addr, 0x04, 1);
218 1.2 jmcneill WR4(tag, addr, 0x7a, 0);
219 1.2 jmcneill WR2(tag, addr, 0x08, 0);
220 1.2 jmcneill WR1(tag, addr, 0x03, 1);
221 1.2 jmcneill WR1(tag, addr, 0x6e, 0);
222 1.2 jmcneill iic_release_bus(tag, 0);
223 1.2 jmcneill
224 1.2 jmcneill avenc_set_volume(0x8e, 0x8e);
225 1.1 jmcneill }
226 1.1 jmcneill
227 1.2 jmcneill
228 1.1 jmcneill static int
229 1.1 jmcneill avenc_match(device_t parent, cfdata_t match, void *aux)
230 1.1 jmcneill {
231 1.1 jmcneill struct i2c_attach_args *ia = aux;
232 1.1 jmcneill
233 1.1 jmcneill return ia->ia_addr == AVENC_ADDR;
234 1.1 jmcneill }
235 1.1 jmcneill
236 1.1 jmcneill static void
237 1.1 jmcneill avenc_attach(device_t parent, device_t self, void *aux)
238 1.1 jmcneill {
239 1.1 jmcneill struct i2c_attach_args *ia = aux;
240 1.1 jmcneill i2c_tag_t tag = ia->ia_tag;
241 1.1 jmcneill i2c_addr_t addr = ia->ia_addr;
242 1.1 jmcneill
243 1.1 jmcneill KASSERT(device_unit(self) == 0);
244 1.1 jmcneill
245 1.1 jmcneill aprint_naive("\n");
246 1.1 jmcneill aprint_normal(": A/V Encoder\n");
247 1.1 jmcneill
248 1.1 jmcneill if (AVENC_DEBUG) {
249 1.2 jmcneill avenc_dump_regs("avenc pre ", tag, addr);
250 1.1 jmcneill }
251 1.1 jmcneill
252 1.1 jmcneill avenc_tag = tag;
253 1.1 jmcneill avenc_addr = addr;
254 1.2 jmcneill
255 1.2 jmcneill avenc_init(tag, addr);
256 1.2 jmcneill
257 1.2 jmcneill if (AVENC_DEBUG) {
258 1.2 jmcneill avenc_dump_regs("avenc post", tag, addr);
259 1.2 jmcneill }
260 1.1 jmcneill }
261 1.1 jmcneill
262 1.1 jmcneill CFATTACH_DECL_NEW(avenc, 0, avenc_match, avenc_attach, NULL, NULL);
263