| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: wiifb.c,v 1.5 2024/01/27 17:44:37 hgutch Exp $ */ | | 1 | /* $NetBSD: wiifb.c,v 1.6 2024/02/05 23:48:35 jmcneill Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> | | 4 | * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -17,43 +17,50 @@ | | | @@ -17,43 +17,50 @@ |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. | | 26 | * SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: wiifb.c,v 1.5 2024/01/27 17:44:37 hgutch Exp $"); | | 30 | __KERNEL_RCSID(0, "$NetBSD: wiifb.c,v 1.6 2024/02/05 23:48:35 jmcneill Exp $"); |
31 | | | 31 | |
32 | #include <sys/param.h> | | 32 | #include <sys/param.h> |
33 | #include <sys/bus.h> | | 33 | #include <sys/bus.h> |
34 | #include <sys/device.h> | | 34 | #include <sys/device.h> |
35 | #include <sys/systm.h> | | 35 | #include <sys/systm.h> |
36 | | | 36 | |
37 | #include <machine/wii.h> | | 37 | #include <machine/wii.h> |
38 | | | 38 | |
39 | #include <dev/videomode/videomode.h> | | 39 | #include <dev/videomode/videomode.h> |
40 | #include <dev/wsfb/genfbvar.h> | | 40 | #include <dev/wsfb/genfbvar.h> |
41 | | | 41 | |
42 | #include "mainbus.h" | | 42 | #include "mainbus.h" |
43 | #include "vireg.h" | | 43 | #include "vireg.h" |
44 | #include "viio.h" | | 44 | #include "viio.h" |
45 | | | 45 | |
46 | #define WIIFB_ERROR_BLINK_INTERVAL 1000000 | | 46 | #define WIIFB_ERROR_BLINK_INTERVAL 1000000 |
| | | 47 | |
| | | 48 | #define WIIFB_TOP_BOTTOM_BORDER 16 |
| | | 49 | #define WIIFB_EFFECTIVE_START(p, w) \ |
| | | 50 | ((uintptr_t)(p) + WIIFB_TOP_BOTTOM_BORDER * (w) * 2) |
| | | 51 | #define WIIFB_EFFECTIVE_HEIGHT(h) \ |
| | | 52 | ((h) - WIIFB_TOP_BOTTOM_BORDER * 2) |
| | | 53 | |
47 | | | 54 | |
48 | struct wiifb_mode { | | 55 | struct wiifb_mode { |
49 | const char * name; | | 56 | const char * name; |
50 | u_int width; | | 57 | u_int width; |
51 | u_int height; | | 58 | u_int height; |
52 | u_int lines; | | 59 | u_int lines; |
53 | }; | | 60 | }; |
54 | | | 61 | |
55 | static uint32_t wiifb_devcmap[16] = { | | 62 | static uint32_t wiifb_devcmap[16] = { |
56 | 0x00800080, /* Black */ | | 63 | 0x00800080, /* Black */ |
57 | 0x1dff1d6b, /* Blue */ | | 64 | 0x1dff1d6b, /* Blue */ |
58 | 0x4b554b4a, /* Green */ | | 65 | 0x4b554b4a, /* Green */ |
59 | 0x80808080, /* Cyan */ | | 66 | 0x80808080, /* Cyan */ |
| @@ -141,46 +148,59 @@ static int | | | @@ -141,46 +148,59 @@ static int |
141 | wiifb_match(device_t parent, cfdata_t cf, void *aux) | | 148 | wiifb_match(device_t parent, cfdata_t cf, void *aux) |
142 | { | | 149 | { |
143 | struct mainbus_attach_args *maa = aux; | | 150 | struct mainbus_attach_args *maa = aux; |
144 | | | 151 | |
145 | return strcmp(maa->maa_name, "genfb") == 0; | | 152 | return strcmp(maa->maa_name, "genfb") == 0; |
146 | } | | 153 | } |
147 | | | 154 | |
148 | static void | | 155 | static void |
149 | wiifb_attach(device_t parent, device_t self, void *aux) | | 156 | wiifb_attach(device_t parent, device_t self, void *aux) |
150 | { | | 157 | { |
151 | struct wiifb_softc *sc = device_private(self); | | 158 | struct wiifb_softc *sc = device_private(self); |
152 | prop_dictionary_t dict = device_properties(self); | | 159 | prop_dictionary_t dict = device_properties(self); |
153 | struct mainbus_attach_args *maa = aux; | | 160 | struct mainbus_attach_args *maa = aux; |
| | | 161 | u_int offset; |
| | | 162 | uint32_t *p; |
154 | int error; | | 163 | int error; |
155 | | | 164 | |
156 | sc->sc_gen.sc_dev = self; | | 165 | sc->sc_gen.sc_dev = self; |
157 | sc->sc_bst = maa->maa_bst; | | 166 | sc->sc_bst = maa->maa_bst; |
158 | error = bus_space_map(sc->sc_bst, maa->maa_addr, VI_SIZE, 0, | | 167 | error = bus_space_map(sc->sc_bst, maa->maa_addr, VI_SIZE, 0, |
159 | &sc->sc_bsh); | | 168 | &sc->sc_bsh); |
160 | if (error != 0) { | | 169 | if (error != 0) { |
161 | panic("couldn't map registers"); | | 170 | panic("couldn't map registers"); |
162 | } | | 171 | } |
163 | sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true); | | 172 | sc->sc_bits = mapiodev(XFB_START, XFB_SIZE, true); |
164 | | | 173 | |
| | | 174 | /* |
| | | 175 | * Paint the entire FB black. Use 4-byte accesses as the Wii will |
| | | 176 | * ignore 1- and 2- byte writes to uncached memory. |
| | | 177 | */ |
| | | 178 | for (p = sc->sc_bits, offset = 0; |
| | | 179 | offset < XFB_SIZE; |
| | | 180 | offset += 4, p++) { |
| | | 181 | *p = 0x00800080; |
| | | 182 | } |
| | | 183 | |
165 | wiifb_init(sc); | | 184 | wiifb_init(sc); |
166 | wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced); | | 185 | wiifb_set_mode(sc, sc->sc_format, sc->sc_interlaced); |
167 | | | 186 | |
168 | prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width); | | 187 | prop_dictionary_set_uint32(dict, "width", sc->sc_curmode->width); |
169 | prop_dictionary_set_uint32(dict, "height", sc->sc_curmode->height); | | 188 | prop_dictionary_set_uint32(dict, "height", |
| | | 189 | WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height)); |
170 | prop_dictionary_set_uint8(dict, "depth", 16); | | 190 | prop_dictionary_set_uint8(dict, "depth", 16); |
171 | prop_dictionary_set_uint32(dict, "address", XFB_START); | | 191 | prop_dictionary_set_uint32(dict, "address", XFB_START); |
172 | prop_dictionary_set_uint32(dict, "virtual_address", | | 192 | prop_dictionary_set_uint32(dict, "virtual_address", |
173 | (uintptr_t)sc->sc_bits); | | 193 | WIIFB_EFFECTIVE_START(sc->sc_bits, sc->sc_curmode->width)); |
174 | prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap); | | 194 | prop_dictionary_set_uint64(dict, "devcmap", (uintptr_t)wiifb_devcmap); |
175 | | | 195 | |
176 | genfb_init(&sc->sc_gen); | | 196 | genfb_init(&sc->sc_gen); |
177 | | | 197 | |
178 | aprint_naive("\n"); | | 198 | aprint_naive("\n"); |
179 | aprint_normal(": %s\n", sc->sc_curmode->name); | | 199 | aprint_normal(": %s\n", sc->sc_curmode->name); |
180 | | | 200 | |
181 | genfb_cnattach(); | | 201 | genfb_cnattach(); |
182 | prop_dictionary_set_bool(dict, "is_console", true); | | 202 | prop_dictionary_set_bool(dict, "is_console", true); |
183 | genfb_attach(&sc->sc_gen, &wiifb_ops); | | 203 | genfb_attach(&sc->sc_gen, &wiifb_ops); |
184 | } | | 204 | } |
185 | | | 205 | |
186 | static void | | 206 | static void |
| @@ -202,32 +222,26 @@ wiifb_init(struct wiifb_softc *sc) | | | @@ -202,32 +222,26 @@ wiifb_init(struct wiifb_softc *sc) |
202 | | | 222 | |
203 | /* Reset video interface. */ | | 223 | /* Reset video interface. */ |
204 | WR2(sc, VI_DCR, dcr | VI_DCR_RST); | | 224 | WR2(sc, VI_DCR, dcr | VI_DCR_RST); |
205 | WR2(sc, VI_DCR, dcr & ~VI_DCR_RST); | | 225 | WR2(sc, VI_DCR, dcr & ~VI_DCR_RST); |
206 | } | | 226 | } |
207 | | | 227 | |
208 | static void | | 228 | static void |
209 | wiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced) | | 229 | wiifb_set_mode(struct wiifb_softc *sc, uint8_t format, bool interlaced) |
210 | { | | 230 | { |
211 | u_int modeidx; | | 231 | u_int modeidx; |
212 | u_int strides, reads; | | 232 | u_int strides, reads; |
213 | | | 233 | |
214 | modeidx = WIIFB_MODE_INDEX(format, interlaced); | | 234 | modeidx = WIIFB_MODE_INDEX(format, interlaced); |
215 | if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) { | | | |
216 | panic("Unsupported format (0x%x) / interlaced (%d) settings", | | | |
217 | sc->sc_format, sc->sc_interlaced); | | | |
218 | } | | | |
219 | sc->sc_curmode = &wiifb_modes[modeidx]; | | | |
220 | | | | |
221 | if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) { | | 235 | if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 1)) { |
222 | /* NTSC 480i Magic numbers from YAGCD. */ | | 236 | /* NTSC 480i Magic numbers from YAGCD. */ |
223 | WR2(sc, VI_VTR, 0x0f06); | | 237 | WR2(sc, VI_VTR, 0x0f06); |
224 | WR4(sc, VI_HTR0, 0x476901AD); | | 238 | WR4(sc, VI_HTR0, 0x476901AD); |
225 | WR4(sc, VI_HTR1, 0x02EA5140); | | 239 | WR4(sc, VI_HTR1, 0x02EA5140); |
226 | WR4(sc, VI_VTO, 0x00030018); | | 240 | WR4(sc, VI_VTO, 0x00030018); |
227 | WR4(sc, VI_VTE, 0x00020019); | | 241 | WR4(sc, VI_VTE, 0x00020019); |
228 | WR4(sc, VI_BBOI, 0x410C410C); | | 242 | WR4(sc, VI_BBOI, 0x410C410C); |
229 | WR4(sc, VI_BBEI, 0x40ED40ED); | | 243 | WR4(sc, VI_BBEI, 0x40ED40ED); |
230 | } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) { | | 244 | } else if (modeidx == WIIFB_MODE_INDEX(VI_DCR_FMT_NTSC, 0)) { |
231 | /* NTSC 480p */ | | 245 | /* NTSC 480p */ |
232 | WR2(sc, VI_VTR, 0x1e0c); | | 246 | WR2(sc, VI_VTR, 0x1e0c); |
233 | WR4(sc, VI_HTR0, 0x476901ad); | | 247 | WR4(sc, VI_HTR0, 0x476901ad); |
| @@ -243,26 +257,32 @@ wiifb_set_mode(struct wiifb_softc *sc, u | | | @@ -243,26 +257,32 @@ wiifb_set_mode(struct wiifb_softc *sc, u |
243 | WR4(sc, VI_HTR1, 0x02f85640); | | 257 | WR4(sc, VI_HTR1, 0x02f85640); |
244 | WR4(sc, VI_VTO, 0x00010023); | | 258 | WR4(sc, VI_VTO, 0x00010023); |
245 | WR4(sc, VI_VTE, 0x00000024); | | 259 | WR4(sc, VI_VTE, 0x00000024); |
246 | WR4(sc, VI_BBOI, 0x4d2b4d6d); | | 260 | WR4(sc, VI_BBOI, 0x4d2b4d6d); |
247 | WR4(sc, VI_BBEI, 0x4d8a4d4c); | | 261 | WR4(sc, VI_BBEI, 0x4d8a4d4c); |
248 | } else { | | 262 | } else { |
249 | /* | | 263 | /* |
250 | * Display mode is not supported. Blink the slot LED to | | 264 | * Display mode is not supported. Blink the slot LED to |
251 | * indicate failure. | | 265 | * indicate failure. |
252 | */ | | 266 | */ |
253 | wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL); | | 267 | wii_slot_led_blink(WIIFB_ERROR_BLINK_INTERVAL); |
254 | } | | 268 | } |
255 | | | 269 | |
| | | 270 | if (modeidx >= WIIFB_NMODES || wiifb_modes[modeidx].name == NULL) { |
| | | 271 | panic("Unsupported format (0x%x) / interlaced (%d) settings", |
| | | 272 | sc->sc_format, sc->sc_interlaced); |
| | | 273 | } |
| | | 274 | sc->sc_curmode = &wiifb_modes[modeidx]; |
| | | 275 | |
256 | /* Picture configuration */ | | 276 | /* Picture configuration */ |
257 | strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32); | | 277 | strides = (sc->sc_curmode->width * 2) / (interlaced ? 16 : 32); |
258 | reads = (sc->sc_curmode->width * 2) / 32; | | 278 | reads = (sc->sc_curmode->width * 2) / 32; |
259 | WR2(sc, VI_PICCONF, | | 279 | WR2(sc, VI_PICCONF, |
260 | __SHIFTIN(strides, VI_PICCONF_STRIDES) | | | 280 | __SHIFTIN(strides, VI_PICCONF_STRIDES) | |
261 | __SHIFTIN(reads, VI_PICCONF_READS)); | | 281 | __SHIFTIN(reads, VI_PICCONF_READS)); |
262 | | | 282 | |
263 | /* Horizontal scaler configuration */ | | 283 | /* Horizontal scaler configuration */ |
264 | if (interlaced) { | | 284 | if (interlaced) { |
265 | WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP)); | | 285 | WR2(sc, VI_HSR, __SHIFTIN(256, VI_HSR_STP)); |
266 | } else { | | 286 | } else { |
267 | WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN); | | 287 | WR2(sc, VI_HSR, __SHIFTIN(244, VI_HSR_STP) | VI_HSR_HS_EN); |
268 | } | | 288 | } |
| @@ -312,31 +332,32 @@ wiifb_ioctl(void *v, void *vs, u_long cm | | | @@ -312,31 +332,32 @@ wiifb_ioctl(void *v, void *vs, u_long cm |
312 | *(u_int *)data = WSDISPLAY_TYPE_HOLLYWOOD; | | 332 | *(u_int *)data = WSDISPLAY_TYPE_HOLLYWOOD; |
313 | return 0; | | 333 | return 0; |
314 | case WSDISPLAYIO_GET_BUSID: | | 334 | case WSDISPLAYIO_GET_BUSID: |
315 | busid = data; | | 335 | busid = data; |
316 | busid->bus_type = WSDISPLAYIO_BUS_SOC; | | 336 | busid->bus_type = WSDISPLAYIO_BUS_SOC; |
317 | return 0; | | 337 | return 0; |
318 | case WSDISPLAYIO_GET_FBINFO: | | 338 | case WSDISPLAYIO_GET_FBINFO: |
319 | fbi = data; | | 339 | fbi = data; |
320 | /* | | 340 | /* |
321 | * rasops info does not match the pixel encoding due to our | | 341 | * rasops info does not match the pixel encoding due to our |
322 | * devcmap, so fill out fbinfo manually instead of relying | | 342 | * devcmap, so fill out fbinfo manually instead of relying |
323 | * on wsdisplayio_get_fbinfo. | | 343 | * on wsdisplayio_get_fbinfo. |
324 | */ | | 344 | */ |
325 | fbi->fbi_fbsize = XFB_SIZE; | | | |
326 | fbi->fbi_fboffset = 0; | | 345 | fbi->fbi_fboffset = 0; |
327 | fbi->fbi_width = sc->sc_curmode->width; | | 346 | fbi->fbi_width = sc->sc_curmode->width; |
328 | fbi->fbi_height = sc->sc_curmode->height; | | 347 | fbi->fbi_height = |
| | | 348 | WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height); |
329 | fbi->fbi_stride = fbi->fbi_width * 2; | | 349 | fbi->fbi_stride = fbi->fbi_width * 2; |
| | | 350 | fbi->fbi_fbsize = fbi->fbi_height * fbi->fbi_stride; |
330 | fbi->fbi_bitsperpixel = 16; | | 351 | fbi->fbi_bitsperpixel = 16; |
331 | fbi->fbi_pixeltype = WSFB_YUY2; | | 352 | fbi->fbi_pixeltype = WSFB_YUY2; |
332 | fbi->fbi_flags = WSFB_VRAM_IS_RAM; | | 353 | fbi->fbi_flags = WSFB_VRAM_IS_RAM; |
333 | return 0; | | 354 | return 0; |
334 | | | 355 | |
335 | case WSDISPLAYIO_SVIDEO: | | 356 | case WSDISPLAYIO_SVIDEO: |
336 | video = *(u_int *)data; | | 357 | video = *(u_int *)data; |
337 | switch (video) { | | 358 | switch (video) { |
338 | case WSDISPLAYIO_VIDEO_OFF: | | 359 | case WSDISPLAYIO_VIDEO_OFF: |
339 | out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) | | | 360 | out32(HW_VIDIM, __SHIFTIN(7, VIDIM_Y) | |
340 | __SHIFTIN(7, VIDIM_C) | | | 361 | __SHIFTIN(7, VIDIM_C) | |
341 | VIDIM_E); | | 362 | VIDIM_E); |
342 | return 0; | | 363 | return 0; |
| @@ -374,21 +395,27 @@ wiifb_ioctl(void *v, void *vs, u_long cm | | | @@ -374,21 +395,27 @@ wiifb_ioctl(void *v, void *vs, u_long cm |
374 | default: | | 395 | default: |
375 | return EINVAL; | | 396 | return EINVAL; |
376 | } | | 397 | } |
377 | return 0; | | 398 | return 0; |
378 | } | | 399 | } |
379 | | | 400 | |
380 | return EPASSTHROUGH; | | 401 | return EPASSTHROUGH; |
381 | } | | 402 | } |
382 | | | 403 | |
383 | static paddr_t | | 404 | static paddr_t |
384 | wiifb_mmap(void *v, void *vs, off_t off, int prot) | | 405 | wiifb_mmap(void *v, void *vs, off_t off, int prot) |
385 | { | | 406 | { |
386 | struct wiifb_softc *sc = v; | | 407 | struct wiifb_softc *sc = v; |
| | | 408 | bus_addr_t start; |
| | | 409 | bus_size_t size; |
| | | 410 | |
| | | 411 | start = WIIFB_EFFECTIVE_START(XFB_START, sc->sc_curmode->width); |
| | | 412 | size = WIIFB_EFFECTIVE_HEIGHT(sc->sc_curmode->height) * |
| | | 413 | sc->sc_curmode->width * 2; |
387 | | | 414 | |
388 | if (off < 0 || off >= XFB_SIZE) { | | 415 | if (off < 0 || off >= size) { |
389 | return -1; | | 416 | return -1; |
390 | } | | 417 | } |
391 | | | 418 | |
392 | return bus_space_mmap(sc->sc_bst, XFB_START, off, prot, | | 419 | return bus_space_mmap(sc->sc_bst, start, off, prot, |
393 | BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE); | | 420 | BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE); |
394 | } | | 421 | } |