1/* $NetBSD: wss_acpi.c,v 1.38 2021/10/07 00:11:08 uwe Exp $ */
2
3/*
4 * Copyright (c) 2002 Jared D. 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. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: wss_acpi.c,v 1.38 2021/10/07 00:11:08 uwe Exp $");
30
31#include <sys/param.h>
32#include <sys/audioio.h>
33#include <sys/device.h>
34#include <sys/systm.h>
35
36#include <dev/audio/audio_if.h>
37
38#include <dev/acpi/acpivar.h>
39
40#include <dev/isa/isadmavar.h>
41#include <dev/isa/ad1848var.h>
42#include <dev/isa/wssreg.h>
43#include <dev/isa/wssvar.h>
44
45static int	wss_acpi_match(device_t, cfdata_t, void *);
46static void	wss_acpi_attach(device_t, device_t, void *);
47
48CFATTACH_DECL_NEW(wss_acpi, sizeof(struct wss_softc), wss_acpi_match,
49    wss_acpi_attach, NULL, NULL);
50
51/*
52 * Supported device IDs
53 */
54
55struct wss_acpi_hint {
56	char idstr[8];
57	int io_region_idx_ad1848;	/* which region index is the DAC?  */
58	int io_region_idx_opl;		/* which region index is the OPL?  */
59	int offset_ad1848;		/* offset from start of DAC region */
60};
61
62static const struct wss_acpi_hint wss_acpi_hints[] = {
63	{ "NMX2210", 1, 2, WSS_CODEC },
64	{ "CSC0000", 0, 1, 0 },		/* Dell Latitude CPi */
65	{ "CSC0100", 0, 1, 0 },		/* CS4610 with CS4236 codec */
66};
67
68static int wss_acpi_hints_index (const char *);
69
70static int
71wss_acpi_hints_index(const char *idstr)
72{
73	int idx;
74
75	if (idstr == NULL)
76		return -1;
77	for (idx = 0; idx < __arraycount(wss_acpi_hints); idx++) {
78		if (!strcmp(wss_acpi_hints[idx].idstr, idstr))
79			return idx;
80	}
81
82	return -1;
83}
84
85/*
86 * wss_acpi_match: autoconf(9) match routine
87 */
88static int
89wss_acpi_match(device_t parent, cfdata_t match, void *aux)
90{
91	struct acpi_attach_args *aa = aux;
92
93	if ((aa->aa_node->ad_devinfo->Valid & ACPI_VALID_HID) == 0)
94		return 0;
95
96	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE ||
97	    wss_acpi_hints_index(aa->aa_node->ad_devinfo->HardwareId.String) == -1)
98		return 0;
99
100	return 1;
101}
102
103/*
104 * wss_acpi_attach: autoconf(9) attach routine
105 */
106static void
107wss_acpi_attach(device_t parent, device_t self, void *aux)
108{
109	struct wss_softc *sc = device_private(self);
110	struct acpi_attach_args *aa = aux;
111	struct acpi_resources res;
112	struct acpi_io *dspio, *oplio;
113	struct acpi_irq *irq;
114	struct acpi_drq *playdrq, *recdrq;
115	struct audio_attach_args arg;
116	ACPI_STATUS rv;
117	const struct wss_acpi_hint *wah;
118
119	sc->sc_ad1848.sc_ad1848.sc_dev = self;
120	wah = &wss_acpi_hints[
121	    wss_acpi_hints_index(aa->aa_node->ad_devinfo->HardwareId.String)];
122
123	/* Parse our resources */
124	rv = acpi_resource_parse(self,
125	    aa->aa_node->ad_handle, "_CRS", &res,
126	    &acpi_resource_parse_ops_default);
127	if (ACPI_FAILURE(rv))
128		return;
129
130	/* Find and map our i/o registers */
131	sc->sc_iot = aa->aa_iot;
132	dspio = acpi_res_io(&res, wah->io_region_idx_ad1848);
133	oplio = acpi_res_io(&res, wah->io_region_idx_opl);
134	if (dspio == NULL || oplio == NULL) {
135		aprint_error_dev(self,
136		    "unable to find i/o registers resource\n");
137		goto out;
138	}
139	if (bus_space_map(sc->sc_iot, dspio->ar_base, dspio->ar_length,
140	    0, &sc->sc_ioh) != 0) {
141		aprint_error_dev(self, "unable to map i/o registers\n");
142		goto out;
143	}
144	if (bus_space_map(sc->sc_iot, oplio->ar_base, oplio->ar_length,
145	    0, &sc->sc_opl_ioh) != 0) {
146		aprint_error_dev(self, "unable to map opl i/o registers\n");
147		goto out;
148	}
149
150	sc->wss_ic = aa->aa_ic;
151
152	/* Find our IRQ */
153	irq = acpi_res_irq(&res, 0);
154	if (irq == NULL) {
155		aprint_error_dev(self, "unable to find irq resource\n");
156		/* XXX bus_space_unmap */
157		goto out;
158	}
159	sc->wss_irq = irq->ar_irq;
160
161	/* Find our playback and record DRQs */
162	playdrq = acpi_res_drq(&res, 0);
163	recdrq = acpi_res_drq(&res, 1);
164	if (playdrq == NULL || recdrq == NULL) {
165		aprint_error_dev(self, " unable to find drq resources\n");
166		/* XXX bus_space_unmap */
167		goto out;
168	}
169	sc->wss_playdrq = playdrq->ar_drq;
170	sc->wss_recdrq = recdrq->ar_drq;
171
172	sc->sc_ad1848.sc_ad1848.sc_iot = sc->sc_iot;
173	bus_space_subregion(sc->sc_iot, sc->sc_ioh, wah->offset_ad1848, 4,
174	    &sc->sc_ad1848.sc_ad1848.sc_ioh);
175
176	/* Look for the ad1848 */
177	if (!ad1848_isa_probe(&sc->sc_ad1848)) {
178		aprint_error_dev(self, "ad1848 probe failed\n");
179		/* XXX cleanup */
180		goto out;
181	}
182
183	/* Attach our wss device */
184	aprint_naive("%s", device_xname(self));
185	aprint_normal("%s", device_xname(self));
186	wssattach(sc);
187
188	arg.type = AUDIODEV_TYPE_OPL;
189	arg.hwif = 0;
190	arg.hdl = 0;
191	config_found(self, &arg, audioprint, CFARGS(.iattr = "wss"));
192
193 out:
194	acpi_resource_cleanup(&res);
195}
196